Description:
Multi-signature wallet contract requiring multiple confirmations for transaction execution.
Blockchain: Ethereum
Source Code: View Code On The Blockchain
Solidity Source Code:
{{
"language": "Solidity",
"sources": {
"lib/yieldnest-vault/src/hooks/FeeHooks.sol": {
"content": "// SPDX-License-Identifier: BSD-3-Clause
pragma solidity ^0.8.24;
import {Ownable, ERC20} from "src/Common.sol";
import {IVault} from "src/interface/IVault.sol";
import {IHooks} from "src/interface/IHooks.sol";
import {IFeeHooks} from "src/interface/IFeeHooks.sol";
import {Math} from "src/Common.sol";
import {Vault} from "src/Vault.sol";
/**
* @title FeeHooks
* @notice FeeHooks for the Vault
* @dev This contract gets callback from the vault it's attached to
*/
contract FeeHooks is Ownable, IHooks, IFeeHooks {
using Math for uint256;
/// @notice The performance fee rate denominated in ether (1e18 = 100%)
uint256 public performanceFee;
/// @notice The address that receives performance fees
address public performanceFeeRecipient;
/// @notice The denominator used for fee calculations (1 ether = 100%)
uint256 public constant FEE_DENOMINATOR = 1 ether;
/// @notice The vault contract that this hooks contract is attached to
IVault public immutable VAULT;
/// @notice The configuration struct containing hook permissions
Config public config;
/**
* @notice Constructor
* @param vault_ The address of the Vault to which this hooks contract is attached
* @param owner_ The address of the owner of the contract
* @param performanceFee_ The performance fee to be charged(denominated in 1e18)
* @param performanceFeeRecipient_ The address of the performance fee recipient
* @param config_ The hooks config struct
*/
constructor(
address vault_,
address owner_,
uint256 performanceFee_,
address performanceFeeRecipient_,
Config memory config_
) Ownable(owner_) {
VAULT = IVault(payable(vault_));
if (performanceFee_ > FEE_DENOMINATOR) revert InvalidPerformanceFee();
if (performanceFeeRecipient_ == address(0)) revert InvalidPerformanceFeeRecipient();
performanceFee = performanceFee_;
performanceFeeRecipient = performanceFeeRecipient_;
config = config_;
}
/**
* @notice Returns the name of the hooks module
* @return The name of the hooks module
*/
function name() external pure virtual returns (string memory) {
return "PerformanceFeeHooks";
}
/**
* @notice Modifier to ensure that the caller is the Vault
*/
modifier onlyVault() {
if (msg.sender != address(VAULT)) revert CallerNotVault();
_;
}
/**
* @notice After process accounting hook function
* @dev This hook is called after the totalBaseAssets is updated
* @dev This hook is mints the shares corresponding to the performance fee to the performanceFeeRecipient
* @dev Computations are done in the base asset for maximum precision
* @param params The after process accounting parameters
*/
function afterProcessAccounting(AfterProcessAccountingParams calldata params) external virtual onlyVault {
if (VAULT.alwaysComputeTotalAssets()) {
/**
* FeeHooks MUST NOT be called when alwaysComputeTotalAssets is enabled.
*
* alwaysComputeTotalAssets = true
* => _getVaultStorage().totalAssets has a lagging *undefined* value.
* => params.totalBaseAssetsAfterAccounting - params.totalBaseAssetsBeforeAccounting is *undefined*.
* => This delta includes principal and would cause charging fees on principal.
*/
revert AlwaysComputeTotalAssetsIsEnabled();
}
// if there is increase in total base assets, then there is yield earned
if (params.totalBaseAssetsAfterAccounting > params.totalBaseAssetsBeforeAccounting) {
// calculate the yield earned and fees accrued
uint256 yieldEarnedInBaseAsset =
params.totalBaseAssetsAfterAccounting - params.totalBaseAssetsBeforeAccounting;
uint256 feesAccruedInBaseAsset = (yieldEarnedInBaseAsset * performanceFee) / FEE_DENOMINATOR;
if (feesAccruedInBaseAsset > 0) {
if (params.totalBaseAssetsAfterAccounting <= feesAccruedInBaseAsset) {
revert FeesGreaterOrEqualToTotalBaseAssets();
}
// totalBaseAssetsAfterAccounting already includes the fees accrued
// Shares need to be calculated for the rate after fees are substracted.
// Therefore, we apply the formula:
// sharesToMint = (fees * currentShareSupply) / (currentTotalBaseAssets - fees)
uint256 sharesToMint = feesAccruedInBaseAsset.mulDiv(
params.totalSupplyBeforeAccounting,
params.totalBaseAssetsAfterAccounting - feesAccruedInBaseAsset,
Math.Rounding.Floor
);
if (sharesToMint > 0) {
VAULT.mintShares(performanceFeeRecipient, sharesToMint);
emit PerformanceFeeCharged(
performanceFeeRecipient,
sharesToMint,
feesAccruedInBaseAsset,
params.totalBaseAssetsBeforeAccounting,
params.totalBaseAssetsAfterAccounting,
params.totalSupplyBeforeAccounting
);
}
}
}
}
/**
* @notice Before withdraw hook function
* @dev This hook is called before the withdraw is processed
* @dev This hook is called before the shares and assets are updated in the vault
* @param params The withdraw parameters
*/
function beforeWithdraw(WithdrawParams calldata params) external virtual onlyVault {}
/**
* @notice After redeem hook function
* @dev This hook is called after the redeem is processed
* @param params The redeem parameters
*/
function afterRedeem(RedeemParams calldata params) external virtual onlyVault {}
/**
* @notice Before deposit hook function
* @dev This hook is called before the deposit is processed
* @param params The deposit parameters
*/
function beforeDeposit(DepositParams calldata params) external virtual onlyVault {}
/**
* @notice After deposit hook function
* @dev This hook is called after the deposit is processed
* @param params The deposit parameters
*/
function afterDeposit(DepositParams calldata params) external virtual onlyVault {}
/**
* @notice Before mint hook function
* @dev This hook is called before the mint is processed
* @param params The mint parameters
*/
function beforeMint(MintParams calldata params) external virtual onlyVault {}
/**
* @notice After mint hook function
* @dev This hook is called after the mint is processed
* @param params The mint parameters
*/
function afterMint(MintParams calldata params) external virtual onlyVault {}
/**
* @notice Before redeem hook function
* @dev This hook is called before the redeem is processed
* @param params The redeem parameters
*/
function beforeRedeem(RedeemParams calldata params) external virtual onlyVault {}
/**
* @notice After withdraw hook function
* @dev This hook is called after the withdraw is processed
* @param params The withdraw parameters
*/
function afterWithdraw(WithdrawParams calldata params) external virtual onlyVault {}
/**
* @notice Before process accounting hook function
* @dev This hook is called before the accounting is processed
* @param params The before process accounting parameters
*/
function beforeProcessAccounting(BeforeProcessAccountingParams calldata params) external virtual onlyVault {}
/**
* @notice Set the performance fee for the vault gains
* @param performanceFee_ The performance fee to be charged(denominated in 1e18)
*/
function setPerformanceFee(uint256 performanceFee_) external virtual onlyOwner {
if (performanceFee_ > FEE_DENOMINATOR) revert InvalidPerformanceFee();
emit SetPerformanceFee(performanceFee, performanceFee_);
performanceFee = performanceFee_;
}
/**
* @notice Set the performance fee recipient for the vault gains
* @param performanceFeeRecipient_ The address of the performance fee recipient
*/
function setPerformanceFeeRecipient(address performanceFeeRecipient_) external virtual onlyOwner {
if (performanceFeeRecipient_ == address(0)) revert InvalidPerformanceFeeRecipient();
emit SetPerformanceFeeRecipient(performanceFeeRecipient, performanceFeeRecipient_);
performanceFeeRecipient = performanceFeeRecipient_;
}
/**
* @notice Set the config
* @param config_ The config struct
*/
function setConfig(Config memory config_) external virtual onlyOwner {
config = config_;
}
/**
* @notice Get the hooks config
* @return The hooks config struct
*/
function getConfig() external view virtual returns (Config memory) {
return config;
}
}
"
},
"lib/yieldnest-vault/src/Common.sol": {
"content": "/* solhint-disable no-empty-blocks, no-unused-import */
// SPDX-License-Identifier: BSD-3-Clause
pragma solidity ^0.8.24;
import {AccessControlUpgradeable} from
"lib/openzeppelin-contracts-upgradeable/contracts/access/AccessControlUpgradeable.sol";
import {Address} from "lib/openzeppelin-contracts/contracts/utils/Address.sol";
import {ERC20} from "lib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol";
import {ERC20PermitUpgradeable} from
"lib/openzeppelin-contracts-upgradeable/contracts/token/ERC20/extensions/ERC20PermitUpgradeable.sol";
import {ERC20Upgradeable} from "lib/openzeppelin-contracts-upgradeable/contracts/token/ERC20/ERC20Upgradeable.sol";
import {IAccessControl} from "lib/openzeppelin-contracts/contracts/access/IAccessControl.sol";
import {IERC20} from "lib/openzeppelin-contracts/contracts/interfaces/IERC20.sol";
import {IERC20Metadata} from "lib/openzeppelin-contracts/contracts/interfaces/IERC20Metadata.sol";
import {IERC20Permit} from "lib/openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Permit.sol";
import {IERC4626} from "lib/openzeppelin-contracts/contracts/interfaces/IERC4626.sol";
import {Math} from "lib/openzeppelin-contracts/contracts/utils/math/Math.sol";
import {ProxyAdmin} from "lib/openzeppelin-contracts/contracts/proxy/transparent/ProxyAdmin.sol";
import {ReentrancyGuardUpgradeable} from
"lib/openzeppelin-contracts-upgradeable/contracts/utils/ReentrancyGuardUpgradeable.sol";
import {SafeERC20} from "lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol";
import {TimelockController} from "lib/openzeppelin-contracts/contracts/governance/TimelockController.sol";
import {
TransparentUpgradeableProxy,
ITransparentUpgradeableProxy
} from "lib/openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
import {IERC165} from "lib/openzeppelin-contracts/contracts/interfaces/IERC165.sol";
import {Initializable} from "lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol";
import {OwnableUpgradeable} from "lib/openzeppelin-contracts-upgradeable/contracts/access/OwnableUpgradeable.sol";
import {Ownable} from "lib/openzeppelin-contracts/contracts/access/Ownable.sol";
contract Common {}
"
},
"lib/yieldnest-vault/src/interface/IVault.sol": {
"content": "// SPDX-License-Identifier: BSD-3-Clause
pragma solidity ^0.8.24;
import {IERC4626} from "src/Common.sol";
import {IValidator} from "src/interface/IValidator.sol";
import {IHooks} from "src/interface/IHooks.sol";
interface IVault is IERC4626 {
struct VaultStorage {
uint256 totalAssets;
address provider;
address buffer;
bool paused;
uint8 decimals;
bool countNativeAsset;
bool alwaysComputeTotalAssets;
/// @notice The index of the default asset.
/// The default asset is vault.asset(), used for deposit, withdraw, redeem, mint as default.
/// If defaultAssetIndex is 0, the vault will use the base asset as default asset.
uint256 defaultAssetIndex;
}
struct AssetParams {
uint256 index;
bool active;
uint8 decimals;
}
struct AssetUpdateFields {
bool active;
}
struct AssetStorage {
mapping(address => AssetParams) assets;
address[] list;
}
struct OverriddenBaseWithdrawalFeeFields {
/// @notice The base withdrawal fee in basis points (1e8 = 100%) for the user to override
uint64 baseWithdrawalFee;
/// @notice Whether the fee is overridden for the user
bool isOverridden;
}
struct FeeStorage {
/// @notice The base withdrawal fee in basis points (1e8 = 100%)
uint64 baseWithdrawalFee;
mapping(address user => OverriddenBaseWithdrawalFeeFields fields) overriddenBaseWithdrawalFee;
}
struct HooksStorage {
IHooks hooks;
}
enum ParamType {
UINT256,
ADDRESS
}
struct ParamRule {
ParamType paramType;
bool isArray;
address[] allowList;
}
struct FunctionRule {
bool isActive;
ParamRule[] paramRules;
IValidator validator;
}
struct ProcessorStorage {
uint256 lastProcessed;
uint256 lastAccounting;
mapping(address => mapping(bytes4 => FunctionRule)) rules;
}
error Paused();
error Unpaused();
error ZeroAddress();
error ZeroAmount();
error ZeroRate();
error InvalidString();
error InvalidArray();
error ExceededMaxDeposit(address sender, uint256 amount, uint256 maxAssets);
error DefaultAsset();
error AssetNotEmpty(address);
error InvalidAsset(address);
error InvalidTarget(address);
error InvalidDecimals();
error InvalidFunction(address target, bytes4 funcSig);
error DuplicateAsset(address asset);
error ExceededMaxWithdraw(address, uint256, uint256);
error ExceededMaxRedeem(address, uint256, uint256);
error ProcessFailed(bytes, bytes);
error ProcessInvalid(bytes);
error ProviderNotSet();
error BufferNotSet();
error DepositFailed();
error AssetNotActive();
error ExceedsMaxBasisPoints(uint256 value);
error InvalidNativeAssetDecimals(uint256 decimals);
error InvalidAssetDecimals(uint256 decimals);
error InvalidDefaultAssetIndex(uint256 index);
error ExceedsMaxPerformanceFee(uint256 value);
error BaseAsset();
error CallerNotHooks();
error InvalidHooks();
event DepositAsset(
address indexed sender,
address indexed receiver,
address indexed asset,
uint256 assets,
uint256 baseAssets,
uint256 shares
);
event WithdrawAsset(
address indexed sender,
address indexed receiver,
address indexed owner,
address asset,
uint256 assets,
uint256 shares
);
event SetProvider(address indexed provider);
event SetBuffer(address indexed buffer);
event SetAlwaysComputeTotalAssets(bool alwaysComputeTotalAssets);
event NewAsset(address indexed asset, uint256 decimals, uint256 index);
event ProcessSuccess(address[] targets, uint256[] values, bytes[] data);
event Pause(bool paused);
event SetProcessorRule(address indexed target, bytes4, FunctionRule);
event NativeDeposit(uint256 amount);
event ProcessAccounting(uint256 timestamp, uint256 totalAssetsBefore, uint256 totalAssetsAfter);
event UpdateAsset(uint256 indexed index, address indexed asset, AssetUpdateFields fields);
event DeleteAsset(uint256 indexed index, address indexed asset);
event SetBaseWithdrawalFee(uint64 oldFee, uint64 newFee);
event WithdrawalFeeOverridden(address indexed user, uint64 baseWithdrawalFee, bool isOverridden);
event SetHooks(address indexed oldHooks, address indexed newHooks);
// 4626-MAX
function getAssets() external view returns (address[] memory list);
function getAsset(address asset_) external view returns (AssetParams memory);
function hasAsset(address asset_) external view returns (bool);
function getProcessorRule(address contractAddress, bytes4 funcSig) external view returns (FunctionRule memory);
function previewDepositAsset(address assetAddress, uint256 assets) external view returns (uint256);
function depositAsset(address assetAddress, uint256 amount, address receiver) external returns (uint256);
function provider() external view returns (address);
function buffer() external view returns (address);
function totalBaseAssets() external view returns (uint256);
function computeTotalAssets() external view returns (uint256);
function alwaysComputeTotalAssets() external view returns (bool);
// ADMIN
function setProvider(address provider) external;
function setBuffer(address buffer) external;
function setProcessorRule(address target, bytes4 functionSig, FunctionRule memory rule) external;
function setProcessorRules(address[] memory targets, bytes4[] memory functionSigs, FunctionRule[] memory rules)
external;
function addAsset(address asset_, bool active_) external;
function deleteAsset(uint256 index) external;
function pause() external;
function unpause() external;
function hooks() external view returns (IHooks);
function setHooks(address hooks) external;
function mintShares(address recipient, uint256 shares) external;
function withdrawAsset(address asset_, uint256 assets, address receiver, address owner)
external
returns (uint256);
function processAccounting() external;
function processor(address[] calldata targets, uint256[] calldata values, bytes[] calldata data)
external
returns (bytes[] memory);
// FEES
function _feeOnRaw(uint256 amount, address user) external view returns (uint256);
function _feeOnTotal(uint256 amount, address user) external view returns (uint256);
}
"
},
"lib/yieldnest-vault/src/interface/IHooks.sol": {
"content": "// SPDX-License-Identifier: BSD-3-Clause
pragma solidity ^0.8.24;
import {IVault} from "src/interface/IVault.sol";
interface IHooks {
/**
* @notice Parameters for deposit operations
*/
struct DepositParams {
address asset;
uint256 assets;
address caller;
address receiver;
uint256 shares;
uint256 baseAssets;
}
/**
* @notice Parameters for mint operations
*/
struct MintParams {
address asset;
uint256 shares;
address caller;
address receiver;
uint256 assets;
uint256 baseAssets;
}
/**
* @notice Parameters for redeem operations
*/
struct RedeemParams {
address asset;
uint256 shares;
address caller;
address receiver;
address owner;
uint256 assets;
}
/**
* @notice Parameters for withdraw operations
*/
struct WithdrawParams {
address asset;
uint256 assets;
address caller;
address receiver;
address owner;
uint256 shares;
}
/**
* @notice Parameters for before process accounting operations
*/
struct BeforeProcessAccountingParams {
uint256 totalAssetsBeforeAccounting;
uint256 totalSupplyBeforeAccounting;
uint256 totalBaseAssetsBeforeAccounting;
}
/**
* @notice Parameters for after process accounting operations
*/
struct AfterProcessAccountingParams {
uint256 totalAssetsBeforeAccounting;
uint256 totalAssetsAfterAccounting;
uint256 totalSupplyBeforeAccounting;
uint256 totalSupplyAfterAccounting;
uint256 totalBaseAssetsBeforeAccounting;
uint256 totalBaseAssetsAfterAccounting;
}
/**
* @notice Config struct for the hooks
* @dev Each flag is a boolean value that indicates if the corresponding hook function is enabled for the vault
* if the flag is true, the hook function must be called by the vault.
* if the flag is false, the hook function is expected to be a no-op.
*/
struct Config {
bool beforeDeposit;
bool afterDeposit;
bool beforeMint;
bool afterMint;
bool beforeRedeem;
bool afterRedeem;
bool beforeWithdraw;
bool afterWithdraw;
bool beforeProcessAccounting;
bool afterProcessAccounting;
}
error CallerNotVault();
/**
* @notice Returns the name of the hooks module
* @return The name of the hooks module
*/
function name() external view returns (string memory);
/**
* @notice Returns the vault that the hooks are attached to
* @return The vault contract interface
*/
function VAULT() external view returns (IVault);
/**
* @notice Sets the hooks configuration
* @param config_ The configuration struct containing hook permissions
*/
function setConfig(Config memory config_) external;
/**
* @notice Gets the current hooks configuration
* @return The configuration struct containing hook permissions
*/
function getConfig() external view returns (Config memory);
/**
* @notice Hook called before deposit is processed
* @param params The deposit parameters
*/
function beforeDeposit(DepositParams memory params) external;
/**
* @notice Hook called after deposit is processed
* @param params The deposit parameters
*/
function afterDeposit(DepositParams memory params) external;
/**
* @notice Hook called before mint is processed
* @param params The mint parameters
*/
function beforeMint(MintParams memory params) external;
/**
* @notice Hook called after mint is processed
* @param params The mint parameters
*/
function afterMint(MintParams memory params) external;
/**
* @notice Hook called before redeem is processed
* @param params The redeem parameters
*/
function beforeRedeem(RedeemParams memory params) external;
/**
* @notice Hook called after redeem is processed
* @param params The redeem parameters
*/
function afterRedeem(RedeemParams memory params) external;
/**
* @notice Hook called before withdraw is processed
* @param params The withdraw parameters
*/
function beforeWithdraw(WithdrawParams memory params) external;
/**
* @notice Hook called after withdraw is processed
* @param params The withdraw parameters
*/
function afterWithdraw(WithdrawParams memory params) external;
/**
* @notice Hook called before process accounting is executed
* @param params The before process accounting parameters
*/
function beforeProcessAccounting(BeforeProcessAccountingParams memory params) external;
/**
* @notice Hook called after process accounting is executed
* @param params The after process accounting parameters
*/
function afterProcessAccounting(AfterProcessAccountingParams memory params) external;
}
"
},
"lib/yieldnest-vault/src/interface/IFeeHooks.sol": {
"content": "// SPDX-License-Identifier: BSD-3-Clause
pragma solidity ^0.8.24;
import {IVault} from "src/interface/IVault.sol";
interface IFeeHooks {
error InvalidPerformanceFee();
error InvalidPerformanceFeeRecipient();
error AlwaysComputeTotalAssetsIsEnabled();
error FeesGreaterOrEqualToTotalBaseAssets();
event PerformanceFeeCharged(
address indexed recipient,
uint256 sharesMinted,
uint256 performanceFeeAmount,
uint256 totalBaseAssetsBefore,
uint256 totalBaseAssetsAfter,
uint256 totalShares
);
event SetPerformanceFee(uint256 oldFee, uint256 newFee);
event SetPerformanceFeeRecipient(address indexed oldRecipient, address indexed newRecipient);
event WithdrawFeeCharged(address indexed recipient, uint256 sharesMinted, uint256 fees, uint256 assets);
event RedeemFeeCharged(address indexed recipient, uint256 sharesMinted, uint256 shares);
function performanceFee() external view returns (uint256);
function performanceFeeRecipient() external view returns (address);
function setPerformanceFee(uint256 performanceFee_) external;
function setPerformanceFeeRecipient(address performanceFeeRecipient_) external;
}
"
},
"lib/yieldnest-vault/src/Vault.sol": {
"content": "// SPDX-License-Identifier: BSD-3-Clause
pragma solidity ^0.8.24;
import {BaseVault} from "src/BaseVault.sol";
import {FeeMath} from "src/module/FeeMath.sol";
import {VaultLib} from "src/library/VaultLib.sol";
/**
* @title Vault
* @notice Multi-asset vault implementation extending ERC4626 standard
* @dev Provides asset management with fee capabilities and flexible accounting
*
* Key features:
* - Supports multiple deposit assets with common accounting denomination
* - Configurable withdrawal fees managed by fee managers
* - Buffer and strategy allocation for asset management
* - Flexible accounting with real-time or cached asset valuation
* - Role-based access control for administration and fee management
*/
contract Vault is BaseVault {
string public constant VAULT_VERSION = "0.4.0";
bytes32 public constant FEE_MANAGER_ROLE = keccak256("FEE_MANAGER_ROLE");
/**
* @notice Initializes the vault.
* @param admin The address of the admin.
* @param name The name of the vault.
* @param symbol The symbol of the vault.
* @param decimals_ The number of decimals for the vault token.
* @param baseWithdrawalFee_ The base withdrawal fee in basis points (1e8 = 100%).
* @param countNativeAsset_ Whether the vault should count the native asset.
* @param alwaysComputeTotalAssets_ Whether the vault should always compute total assets.
* @param defaultAssetIndex_ The index of the default asset in the asset list.
*/
function initialize(
address admin,
string memory name,
string memory symbol,
uint8 decimals_,
uint64 baseWithdrawalFee_,
bool countNativeAsset_,
bool alwaysComputeTotalAssets_,
uint256 defaultAssetIndex_
) external virtual initializer {
_initialize(
admin, // Address with admin privileges for the vault
name, // Name of the vault token (ERC20)
symbol, // Symbol of the vault token (ERC20)
decimals_, // Decimal precision for the vault token
true, // Start the vault in paused state for safety
countNativeAsset_, // Whether to include native ETH in asset calculations
alwaysComputeTotalAssets_, // Whether to compute assets in real-time vs using cached values
defaultAssetIndex_ // Index of the default asset in the asset list
);
_setBaseWithdrawalFee(baseWithdrawalFee_);
}
//// FEES ////
function _getFeeStorage() internal pure returns (FeeStorage storage) {
return VaultLib.getFeeStorage();
}
/**
* @notice Returns the fee on amount where the fee would get added on top of the amount.
* @param amount The amount on which the fee would get added.
* @param user The address of the user.
* @return The fee amount.
*/
function _feeOnRaw(uint256 amount, address user) public view override returns (uint256) {
return FeeMath.feeOnRaw(amount, _feesToCharge(user));
}
/**
* @notice Returns the fee amount where fee is already included in amount
* @param amount The amount on which the fee is already included.
* @param user The address of the user.
* @return The fee amount.
* @dev Calculates the fee part of an amount `amount` that already includes fees.
* Used in {IERC4626-deposit} and {IERC4626-redeem} operations.
*/
function _feeOnTotal(uint256 amount, address user) public view override returns (uint256) {
return FeeMath.feeOnTotal(amount, _feesToCharge(user));
}
/**
* @notice Returns the fee to charge for a user based on whether the fee is overridden for the user
* @param user The address of the user.
* @return The fee to charge.
*/
function _feesToCharge(address user) internal view returns (uint64) {
FeeStorage storage fees = _getFeeStorage();
bool isFeeOverridenForUser = fees.overriddenBaseWithdrawalFee[user].isOverridden;
if (isFeeOverridenForUser) {
return fees.overriddenBaseWithdrawalFee[user].baseWithdrawalFee;
} else {
return fees.baseWithdrawalFee;
}
}
//// FEES ADMIN ////
/**
* @notice Sets the base withdrawal fee for the vault
* @param baseWithdrawalFee_ The new base withdrawal fee in basis points (1/10000)
* @dev Only callable by accounts with FEE_MANAGER_ROLE
*/
function setBaseWithdrawalFee(uint64 baseWithdrawalFee_) external virtual onlyRole(FEE_MANAGER_ROLE) {
_setBaseWithdrawalFee(baseWithdrawalFee_);
}
/**
* @notice Sets whether the withdrawal fee is exempted for a user
* @param user_ The address of the user
* @param baseWithdrawalFee_ The overridden base withdrawal fee in basis points (1/10000)
* @param toOverride_ Whether to override the withdrawal fee for the user
* @dev Only callable by accounts with FEE_MANAGER_ROLE
*/
function overrideBaseWithdrawalFee(address user_, uint64 baseWithdrawalFee_, bool toOverride_)
external
virtual
onlyRole(FEE_MANAGER_ROLE)
{
_overrideBaseWithdrawalFee(user_, baseWithdrawalFee_, toOverride_);
}
/**
* @notice Internal function to set whether the withdrawal fee is exempted for a user
* @param user_ The address of the user
* @param baseWithdrawalFee_ The overridden base withdrawal fee in basis points (1/10000)
* @param toOverride_ Whether to override the withdrawal fee for the user
*/
function _overrideBaseWithdrawalFee(address user_, uint64 baseWithdrawalFee_, bool toOverride_) internal virtual {
FeeStorage storage fees = _getFeeStorage();
fees.overriddenBaseWithdrawalFee[user_] =
OverriddenBaseWithdrawalFeeFields({baseWithdrawalFee: baseWithdrawalFee_, isOverridden: toOverride_});
emit WithdrawalFeeOverridden(user_, baseWithdrawalFee_, toOverride_);
}
/**
* @dev Internal implementation of setBaseWithdrawalFee
* @param baseWithdrawalFee_ The new base withdrawal fee in basis points (1/10000)
*/
function _setBaseWithdrawalFee(uint64 baseWithdrawalFee_) internal virtual {
if (baseWithdrawalFee_ > FeeMath.BASIS_POINT_SCALE) revert ExceedsMaxBasisPoints(baseWithdrawalFee_);
FeeStorage storage fees = _getFeeStorage();
uint64 oldFee = fees.baseWithdrawalFee;
fees.baseWithdrawalFee = baseWithdrawalFee_;
emit SetBaseWithdrawalFee(oldFee, baseWithdrawalFee_);
}
/**
* @notice Returns the base withdrawal fee
* @return uint64 The base withdrawal fee in basis points (1/10000)
*/
function baseWithdrawalFee() external view returns (uint64) {
return _getFeeStorage().baseWithdrawalFee;
}
/**
* @notice Returns whether the withdrawal fee is exempted for a user
* @param user_ The address of the user
* @return bool Whether the withdrawal fee is exempted for the user
*/
function overriddenBaseWithdrawalFee(address user_)
external
view
returns (OverriddenBaseWithdrawalFeeFields memory)
{
return _getFeeStorage().overriddenBaseWithdrawalFee[user_];
}
}
"
},
"lib/openzeppelin-contracts-upgradeable/contracts/access/AccessControlUpgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (access/AccessControl.sol)
pragma solidity ^0.8.20;
import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol";
import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {ERC165Upgradeable} from "../utils/introspection/ERC165Upgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Contract module that allows children to implement role-based access
* control mechanisms. This is a lightweight version that doesn't allow enumerating role
* members except through off-chain means by accessing the contract event logs. Some
* applications may benefit from on-chain enumerability, for those cases see
* {AccessControlEnumerable}.
*
* Roles are referred to by their `bytes32` identifier. These should be exposed
* in the external API and be unique. The best way to achieve this is by
* using `public constant` hash digests:
*
* ```solidity
* bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
* ```
*
* Roles can be used to represent a set of permissions. To restrict access to a
* function call, use {hasRole}:
*
* ```solidity
* function foo() public {
* require(hasRole(MY_ROLE, msg.sender));
* ...
* }
* ```
*
* Roles can be granted and revoked dynamically via the {grantRole} and
* {revokeRole} functions. Each role has an associated admin role, and only
* accounts that have a role's admin role can call {grantRole} and {revokeRole}.
*
* By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
* that only accounts with this role will be able to grant or revoke other
* roles. More complex role relationships can be created by using
* {_setRoleAdmin}.
*
* WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
* grant and revoke this role. Extra precautions should be taken to secure
* accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
* to enforce additional security measures for this role.
*/
abstract contract AccessControlUpgradeable is Initializable, ContextUpgradeable, IAccessControl, ERC165Upgradeable {
struct RoleData {
mapping(address account => bool) hasRole;
bytes32 adminRole;
}
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
/// @custom:storage-location erc7201:openzeppelin.storage.AccessControl
struct AccessControlStorage {
mapping(bytes32 role => RoleData) _roles;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.AccessControl")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant AccessControlStorageLocation = 0x02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800;
function _getAccessControlStorage() private pure returns (AccessControlStorage storage $) {
assembly {
$.slot := AccessControlStorageLocation
}
}
/**
* @dev Modifier that checks that an account has a specific role. Reverts
* with an {AccessControlUnauthorizedAccount} error including the required role.
*/
modifier onlyRole(bytes32 role) {
_checkRole(role);
_;
}
function __AccessControl_init() internal onlyInitializing {
}
function __AccessControl_init_unchained() internal onlyInitializing {
}
/// @inheritdoc IERC165
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
}
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) public view virtual returns (bool) {
AccessControlStorage storage $ = _getAccessControlStorage();
return $._roles[role].hasRole[account];
}
/**
* @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()`
* is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier.
*/
function _checkRole(bytes32 role) internal view virtual {
_checkRole(role, _msgSender());
}
/**
* @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`
* is missing `role`.
*/
function _checkRole(bytes32 role, address account) internal view virtual {
if (!hasRole(role, account)) {
revert AccessControlUnauthorizedAccount(account, role);
}
}
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) public view virtual returns (bytes32) {
AccessControlStorage storage $ = _getAccessControlStorage();
return $._roles[role].adminRole;
}
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*
* May emit a {RoleGranted} event.
*/
function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
_grantRole(role, account);
}
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*
* May emit a {RoleRevoked} event.
*/
function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
_revokeRole(role, account);
}
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been revoked `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `callerConfirmation`.
*
* May emit a {RoleRevoked} event.
*/
function renounceRole(bytes32 role, address callerConfirmation) public virtual {
if (callerConfirmation != _msgSender()) {
revert AccessControlBadConfirmation();
}
_revokeRole(role, callerConfirmation);
}
/**
* @dev Sets `adminRole` as ``role``'s admin role.
*
* Emits a {RoleAdminChanged} event.
*/
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
AccessControlStorage storage $ = _getAccessControlStorage();
bytes32 previousAdminRole = getRoleAdmin(role);
$._roles[role].adminRole = adminRole;
emit RoleAdminChanged(role, previousAdminRole, adminRole);
}
/**
* @dev Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.
*
* Internal function without access restriction.
*
* May emit a {RoleGranted} event.
*/
function _grantRole(bytes32 role, address account) internal virtual returns (bool) {
AccessControlStorage storage $ = _getAccessControlStorage();
if (!hasRole(role, account)) {
$._roles[role].hasRole[account] = true;
emit RoleGranted(role, account, _msgSender());
return true;
} else {
return false;
}
}
/**
* @dev Attempts to revoke `role` from `account` and returns a boolean indicating if `role` was revoked.
*
* Internal function without access restriction.
*
* May emit a {RoleRevoked} event.
*/
function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {
AccessControlStorage storage $ = _getAccessControlStorage();
if (hasRole(role, account)) {
$._roles[role].hasRole[account] = false;
emit RoleRevoked(role, account, _msgSender());
return true;
} else {
return false;
}
}
}
"
},
"lib/openzeppelin-contracts/contracts/utils/Address.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (utils/Address.sol)
pragma solidity ^0.8.20;
import {Errors} from "./Errors.sol";
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev There's no code at `target` (it is not a contract).
*/
error AddressEmptyCode(address target);
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
if (address(this).balance < amount) {
revert Errors.InsufficientBalance(address(this).balance, amount);
}
(bool success, bytes memory returndata) = recipient.call{value: amount}("");
if (!success) {
_revert(returndata);
}
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason or custom error, it is bubbled
* up by this function (like regular Solidity function calls). However, if
* the call reverted with no returned reason, this function reverts with a
* {Errors.FailedCall} error.
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
if (address(this).balance < value) {
revert Errors.InsufficientBalance(address(this).balance, value);
}
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
* was not a contract or bubbling up the revert reason (falling back to {Errors.FailedCall}) in case
* of an unsuccessful call.
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata
) internal view returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
// only check if target is a contract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
if (returndata.length == 0 && target.code.length == 0) {
revert AddressEmptyCode(target);
}
return returndata;
}
}
/**
* @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
* revert reason or with a default {Errors.FailedCall} error.
*/
function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
return returndata;
}
}
/**
* @dev Reverts with returndata if present. Otherwise reverts with {Errors.FailedCall}.
*/
function _revert(bytes memory returndata) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
assembly ("memory-safe") {
revert(add(returndata, 0x20), mload(returndata))
}
} else {
revert Errors.FailedCall();
}
}
}
"
},
"lib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC20/ERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "./IERC20.sol";
import {IERC20Metadata} from "./extensions/IERC20Metadata.sol";
import {Context} from "../../utils/Context.sol";
import {IERC20Errors} from "../../interfaces/draft-IERC6093.sol";
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
*
* TIP: For a detailed writeup see our guide
* https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* The default value of {decimals} is 18. To change this, you should override
* this function so it returns a different value.
*
* We have followed general OpenZeppelin Contracts guidelines: functions revert
* instead returning `false` on failure. This behavior is nonetheless
* conventional and does not conflict with the expectations of ERC-20
* applications.
*/
abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {
mapping(address account => uint256) private _balances;
mapping(address account => mapping(address spender => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
/**
* @dev Sets the values for {name} and {symbol}.
*
* Both values are immutable: they can only be set once during construction.
*/
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
/**
* @dev Returns the name of the token.
*/
function name() public view virtual returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view virtual returns (string memory) {
return _symbol;
}
/**
* @dev Returns the number of decimals used to get its user representation.
* For example, if `decimals` equals `2`, a balance of `505` tokens should
* be displayed to a user as `5.05` (`505 / 10 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei. This is the default value returned by this function, unless
* it's overridden.
*
* NOTE: This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}.
*/
function decimals() public view virtual returns (uint8) {
return 18;
}
/// @inheritdoc IERC20
function totalSupply() public view virtual returns (uint256) {
return _totalSupply;
}
/// @inheritdoc IERC20
function balanceOf(address account) public view virtual returns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - the caller must have a balance of at least `value`.
*/
function transfer(address to, uint256 value) public virtual returns (bool) {
address owner = _msgSender();
_transfer(owner, to, value);
return true;
}
/// @inheritdoc IERC20
function allowance(address owner, address spender) public view virtual returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* NOTE: If `value` is the maximum `uint256`, the allowance is not updated on
* `transferFrom`. This is semantically equivalent to an infinite approval.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 value) public virtual returns (bool) {
address owner = _msgSender();
_approve(owner, spender, value);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Skips emitting an {Approval} event indicating an allowance update. This is not
* required by the ERC. See {xref-ERC20-_approve-address-address-uint256-bool-}[_approve].
*
* NOTE: Does not update the allowance if the current allowance
* is the maximum `uint256`.
*
* Requirements:
*
* - `from` and `to` cannot be the zero address.
* - `from` must have a balance of at least `value`.
* - the caller must have allowance for ``from``'s tokens of at least
* `value`.
*/
function transferFrom(address from, address to, uint256 value) public virtual returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, value);
_transfer(from, to, value);
return true;
}
/**
* @dev Moves a `value` amount of tokens from `from` to `to`.
*
* This internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* NOTE: This function is not virtual, {_update} should be overridden instead.
*/
function _transfer(address from, address to, uint256 value) internal {
if (from == address(0)) {
revert ERC20InvalidSender(address(0));
}
if (to == address(0)) {
revert ERC20InvalidReceiver(address(0));
}
_update(from, to, value);
}
/**
* @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from`
* (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding
* this function.
*
* Emits a {Transfer} event.
*/
function _update(address from, address to, uint256 value) internal virtual {
if (from == address(0)) {
// Overflow check required: The rest of the code assumes that totalSupply never overflows
_totalSupply += value;
} else {
uint256 fromBalance = _balances[from];
if (fromBalance < value) {
revert ERC20InsufficientBalance(from, fromBalance, value);
}
unchecked {
// Overflow not possible: value <= fromBalance <= totalSupply.
_balances[from] = fromBalance - value;
}
}
if (to == address(0)) {
unchecked {
// Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply.
_totalSupply -= value;
}
} else {
unchecked {
// Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.
_balances[to] += value;
}
}
emit Transfer(from, to, value);
}
/**
* @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0).
* Relies on the `_update` mechanism
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* NOTE: This function is not virtual, {_update} should be overridden instead.
*/
function _mint(address account, uint256 value) internal {
if (account == address(0)) {
revert ERC20InvalidReceiver(address(0));
}
_update(address(0), account, value);
}
/**
* @dev Destroys a `value` amount of tokens from `account`, lowering the total supply.
* Relies on the `_update` mechanism.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* NOTE: This function is not virtual, {_update} should be overridden instead
*/
function _burn(address account, uint256 value) internal {
if (account == address(0)) {
revert ERC20InvalidSender(address(0));
}
_update(account, address(0), value);
}
/**
* @dev Sets `value` as the allowance of `spender` over the `owner`'s tokens.
*
* This internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*
* Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
*/
function _approve(address owner, address spender, uint256 value) internal {
_approve(owner, spender, value, true);
}
/**
* @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event.
*
* By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by
* `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any
* `Approval` event during `transferFrom` operations.
*
* Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to
* true using the following override:
*
* ```solidity
* function _approve(address owner, address spender, uint256 value, bool) internal virtual override {
* super._approve(owner, spender, value, true);
* }
* ```
*
* Requirements are the same as {_approve}.
*/
function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {
if (owner == address(0)) {
revert ERC20InvalidApprover(address(0));
}
if (spender == address(0)) {
revert ERC20InvalidSpender(address(0));
}
_allowances[owner][spender] = value;
if (emitEvent) {
emit Approval(owner, spender, value);
}
}
/**
* @dev Updates `owner`'s allowance for `spender` based on spent `value`.
*
* Does not update the allowance value in case of infinite allowance.
* Revert if not enough allowance is available.
*
* Does not emit an {Approval} event.
*/
function _spendAllowance(address owner, address spender, uint256 value) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance < type(uint256).max) {
if (currentAllowance < value) {
revert ERC20InsufficientAllowance(spender, currentAllowance, value);
}
unchecked {
_approve(owner, spender, currentAllowance - value, false);
}
}
}
}
"
},
"lib/openzeppelin-contracts-upgradeable/contracts/token/ERC20/extensions/ERC20PermitUpgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC20/extensions/ERC20Permit.sol)
pragma solidity ^0.8.20;
import {IERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol";
import {ERC20Upgradeable} from "../ERC20Upgradeable.sol";
import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import {EIP712Upgradeable} from "../../../utils/cryptography/EIP712Upgradeable.sol";
import {NoncesUpgradeable} from "../../../utils/NoncesUpgradeable.sol";
import {Initializable} from "../../../proxy/utils/Initializable.sol";
/**
* @dev Implementation of the ERC-20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[ERC-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC-20 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.
*/
abstract contract ERC20PermitUpgradeable is Initializable, ERC20Upgradeable, IERC20Permit, EIP712Upgradeable, NoncesUpgradeable {
bytes32 private constant PERMIT_TYPEHASH =
keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
/**
* @dev Permit deadline has expired.
*/
error ERC2612ExpiredSignature(uint256 deadline);
/**
* @dev Mismatched signature.
*/
error ERC2612InvalidSigner(address signer, address owner);
/**
* @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 ERC-20 token name.
*/
function __ERC20Permit_init(string memory name) internal onlyInitializing {
__EIP712_init_unchained(name, "1");
}
function __ERC20Permit_init_unchained(string memory) internal onlyInitializing {}
/// @inheritdoc IERC20Permit
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) public virtual {
if (block.timestamp > deadline) {
revert ERC2612ExpiredSignature(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);
if (signer != owner) {
revert ERC2612InvalidSigner(signer, owner);
}
_approve(owner, spender, value);
}
/// @inheritdoc IERC20Permit
function nonces(address owner) public view virtual override(IERC20Permit, NoncesUpgradeable) returns (uint256) {
return super.nonces(owner);
}
/// @inheritdoc IERC20Permit
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view virtual returns (bytes32) {
return _domainSeparatorV4();
}
}
"
},
"lib/openzeppelin-contracts-upgradeable/contracts/token/ERC20/ERC20Upgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC20/ERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {ContextUpgradeable} from "../../utils/ContextUpgradeable.sol";
import {IERC20Errors} from "@openzeppelin/contracts/interfaces/draft-IERC6093.sol";
import {Initializable} from "../../proxy/utils/Initializable.sol";
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
*
* TIP: For a detailed writeup see our guide
* https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* The default value of {decimals} is 18. To change this, you should override
* this function so it returns a different value.
*
* We have followed general OpenZeppelin Contracts guidelines: functions revert
* instead returning `false` on failure. This behavior is nonetheless
* conventional and does not conflict with the expectations of ERC-20
* applications.
*/
abstract contract ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20, IERC20Metadata, IERC20Errors {
/// @custom:storage-location erc7201:openzeppelin.storage.ERC20
struct ERC20Storage {
mapping(address account => uint256) _balances;
mapping(address account => mapping(address spender => uint256)) _allowances;
uint256 _totalSupply;
string _name;
string _symbol;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ERC20")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant ERC20StorageLocation = 0x52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace00;
function _getERC20Storage() private pure returns (ERC20Storage storage $) {
assembly {
$.slot := ERC20StorageLocation
}
}
/**
* @dev Sets the values for {name} and {symbol}.
*
* Both values are immutable: they can only be set once during construction.
*/
function __ERC20_init(string memory name_, string memory symbol_) internal onlyInitializing {
__ERC20_init_unchained(name_, symbol_);
}
function __ERC20_init_unchained(string memory name_, string memory symbol_) internal onlyInitializing {
ERC20Storage storage $ = _getERC20Storage();
$._name = name_;
$._symbol = symbol_;
}
/**
* @dev Returns the name of the token.
*/
function name() public view virtual returns (string memory) {
ERC20St
Submitted on: 2025-10-17 10:40:11
Comments
Log in to comment.
No comments yet.