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": {
"src/rewards/ConvexRewardManager.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.8.29;
import { AbstractRewardManager } from "./AbstractRewardManager.sol";
import { RewardPoolStorage } from "../interfaces/IRewardManager.sol";
import { IConvexRewardPool, IConvexBooster } from "../interfaces/Curve/IConvex.sol";
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import { TokenUtils } from "../utils/TokenUtils.sol";
contract ConvexRewardManager is AbstractRewardManager {
using TokenUtils for ERC20;
function _executeClaim() internal override {
address rewardPool = _getRewardPoolSlot().rewardPool;
require(IConvexRewardPool(rewardPool).getReward(address(this), true));
}
function _withdrawFromPreviousRewardPool(RewardPoolStorage memory oldRewardPool) internal override {
uint256 boosterBalance = ERC20(oldRewardPool.rewardPool).balanceOf(address(this));
require(IConvexRewardPool(oldRewardPool.rewardPool).withdrawAndUnwrap(boosterBalance, true));
}
function _depositIntoNewRewardPool(
address poolToken,
uint256 poolTokens,
RewardPoolStorage memory newRewardPool
)
internal
override
{
uint256 poolId = IConvexRewardPool(newRewardPool.rewardPool).pid();
address booster = IConvexRewardPool(newRewardPool.rewardPool).operator();
ERC20(poolToken).checkApprove(booster, type(uint256).max);
if (poolTokens > 0) {
require(IConvexBooster(booster).deposit(poolId, poolTokens, true));
}
}
}
"
},
"src/rewards/AbstractRewardManager.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.8.29;
import { ReentrancyGuardTransient } from "@openzeppelin/contracts/utils/ReentrancyGuardTransient.sol";
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import { IRewardManager, RewardPoolStorage, VaultRewardState } from "../interfaces/IRewardManager.sol";
import { IYieldStrategy } from "../interfaces/IYieldStrategy.sol";
import { Unauthorized } from "../interfaces/Errors.sol";
import { SHARE_PRECISION, ADDRESS_REGISTRY, YEAR, VIRTUAL_SHARES } from "../utils/Constants.sol";
import { TypeConvert } from "../utils/TypeConvert.sol";
import { IEIP20NonStandard } from "../interfaces/IEIP20NonStandard.sol";
import { TokenUtils } from "../utils/TokenUtils.sol";
abstract contract AbstractRewardManager is IRewardManager, ReentrancyGuardTransient {
using TypeConvert for uint256;
using TokenUtils for ERC20;
modifier onlyUpgradeAdmin() {
if (msg.sender != ADDRESS_REGISTRY.upgradeAdmin()) revert Unauthorized(msg.sender);
_;
}
// Uses custom storage slots to avoid collisions with other contracts
// keccak256("notional.rewardManager.rewardPool")
uint256 private constant REWARD_POOL_SLOT = 0xb1630c9ab375319506d0354f42326eb3b0f3cdb4a34062a84700ebef1ece57f6;
// keccak256("notional.rewardManager.vaultRewardState")
uint256 private constant VAULT_REWARD_STATE_SLOT =
0x5a7d8c54ba64221684d78e9f27720908d8deb4705b6f1336ef8721b7b4329023;
// keccak256("notional.rewardManager.accountRewardDebt")
uint256 private constant ACCOUNT_REWARD_DEBT_SLOT =
0x61d38dd3c50f8b8521fba47d2736bc704c86cafb37f95969db772cc6ddecd0e5;
function _getRewardPoolSlot() internal pure returns (RewardPoolStorage storage store) {
assembly {
store.slot := REWARD_POOL_SLOT
}
}
function _getVaultRewardStateSlot() internal pure returns (VaultRewardState[] storage store) {
assembly {
store.slot := VAULT_REWARD_STATE_SLOT
}
}
function _getAccountRewardDebtSlot()
internal
pure
returns (mapping(address rewardToken => mapping(address account => uint256 rewardDebt)) storage store)
{
assembly {
store.slot := ACCOUNT_REWARD_DEBT_SLOT
}
}
/// @inheritdoc IRewardManager
function migrateRewardPool(
address poolToken,
RewardPoolStorage memory newRewardPool
)
external
override
onlyUpgradeAdmin
nonReentrant
{
// Claim all rewards from the previous reward pool before withdrawing
uint256 effectiveSupplyBefore = IYieldStrategy(address(this)).effectiveSupply();
// Clear the force claim timestamp to ensure that we claim all rewards before migration.
// This value will be set to a new value at the end of this method.
_getRewardPoolSlot().forceClaimAfter = 0;
_claimVaultRewards(effectiveSupplyBefore, _getVaultRewardStateSlot());
RewardPoolStorage memory oldRewardPool = _getRewardPoolSlot();
if (oldRewardPool.rewardPool != address(0)) {
_withdrawFromPreviousRewardPool(oldRewardPool);
// Clear approvals on the old pool.
ERC20(poolToken).checkRevoke(address(oldRewardPool.rewardPool));
}
uint256 poolTokens = ERC20(poolToken).balanceOf(address(this));
_depositIntoNewRewardPool(poolToken, poolTokens, newRewardPool);
// Set the last claim timestamp to the current block timestamp since we're claiming all the rewards
// earlier in this method.
_getRewardPoolSlot().lastClaimTimestamp = uint32(block.timestamp);
_getRewardPoolSlot().rewardPool = newRewardPool.rewardPool;
_getRewardPoolSlot().forceClaimAfter = newRewardPool.forceClaimAfter;
}
/// @inheritdoc IRewardManager
function getRewardSettings()
external
view
override
returns (VaultRewardState[] memory rewardStates, RewardPoolStorage memory rewardPool)
{
rewardStates = _getVaultRewardStateSlot();
rewardPool = _getRewardPoolSlot();
}
/// @inheritdoc IRewardManager
function getRewardDebt(address rewardToken, address account) external view override returns (uint256) {
return _getAccountRewardDebtSlot()[rewardToken][account];
}
/// @inheritdoc IRewardManager
function updateRewardToken(
uint256 index,
address rewardToken,
uint128 emissionRatePerYear,
uint32 endTime
)
external
override
onlyUpgradeAdmin
{
uint256 effectiveSupplyBefore = IYieldStrategy(address(this)).effectiveSupply();
uint256 numRewardStates = _getVaultRewardStateSlot().length;
if (index < numRewardStates) {
VaultRewardState memory state = _getVaultRewardStateSlot()[index];
// Safety check to ensure that the correct token is specified, we can never change the
// token address once set.
require(state.rewardToken == rewardToken);
// Modifies the emission rate on an existing token, direct claims of the token will
// not be affected by the emission rate.
// First accumulate under the old regime up to the current time. Even if the previous
// emissionRatePerYear is zero this will still set the lastAccumulatedTime to the current
// blockTime.
_accumulateSecondaryRewardViaEmissionRate(index, state, effectiveSupplyBefore);
// Save the new emission rates
state.emissionRatePerYear = emissionRatePerYear;
if (state.emissionRatePerYear == 0) {
state.endTime = 0;
} else {
require(block.timestamp < endTime);
state.endTime = endTime;
}
_getVaultRewardStateSlot()[index] = state;
} else if (index == numRewardStates) {
// This sets a new reward token, ensure that the current slot is empty
VaultRewardState[] storage states = _getVaultRewardStateSlot();
// If no emission rate is set then governance is just adding a token that can be claimed
// via the LP tokens without an emission rate. These settings will be left empty and the
// subsequent _claimVaultRewards method will set the initial accumulatedRewardPerVaultShare.
if (0 < emissionRatePerYear) require(block.timestamp < endTime);
states.push(
VaultRewardState({
rewardToken: rewardToken,
lastAccumulatedTime: uint32(block.timestamp),
endTime: endTime,
emissionRatePerYear: emissionRatePerYear,
accumulatedRewardPerVaultShare: 0
})
);
} else {
// Can only append or modify existing tokens
revert();
}
// Claim all vault rewards up to the current time
_claimVaultRewards(effectiveSupplyBefore, _getVaultRewardStateSlot());
emit VaultRewardUpdate(rewardToken, emissionRatePerYear, endTime);
}
/// @notice Claims all the rewards for the entire vault and updates the accumulators. Does not
/// update emission rewarders since those are automatically updated on every account claim.
function claimRewardTokens() external nonReentrant {
// This method is not executed from inside enter or exit vault positions, so this total
// vault shares value is valid.
uint256 effectiveSupplyBefore = IYieldStrategy(address(this)).effectiveSupply();
_claimVaultRewards(effectiveSupplyBefore, _getVaultRewardStateSlot());
}
/// @inheritdoc IRewardManager
function updateAccountRewards(
address account,
uint256 effectiveSupplyBefore,
uint256 accountSharesBefore,
uint256 accountSharesAfter,
bool sharesInEscrow
)
external
returns (uint256[] memory rewards)
{
// Short circuit in this case, no rewards to claim
if (sharesInEscrow && accountSharesAfter > 0) return rewards;
VaultRewardState[] memory state = _getVaultRewardStateSlot();
_claimVaultRewards(effectiveSupplyBefore, state);
rewards = new uint256[](state.length);
for (uint256 i; i < state.length; i++) {
if (sharesInEscrow && accountSharesAfter == 0) {
delete _getAccountRewardDebtSlot()[state[i].rewardToken][account];
continue;
}
if (0 < state[i].emissionRatePerYear) {
// Accumulate any rewards with an emission rate here
_accumulateSecondaryRewardViaEmissionRate(i, state[i], effectiveSupplyBefore);
}
rewards[i] = _claimRewardToken(
state[i].rewardToken,
account,
accountSharesBefore,
accountSharesAfter,
state[i].accumulatedRewardPerVaultShare
);
}
}
/// @notice Executes a claim against the given reward pool type and updates internal
/// rewarder accumulators.
function _claimVaultRewards(uint256 effectiveSupplyBefore, VaultRewardState[] memory state) internal {
RewardPoolStorage memory rewardPool = _getRewardPoolSlot();
if (rewardPool.rewardPool == address(0)) return;
if (block.timestamp < rewardPool.lastClaimTimestamp + rewardPool.forceClaimAfter) return;
uint256[] memory balancesBefore = new uint256[](state.length);
// Run a generic call against the reward pool and then do a balance
// before and after check.
for (uint256 i; i < state.length; i++) {
// Presumes that ETH will never be given out as a reward token.
balancesBefore[i] = ERC20(state[i].rewardToken).balanceOf(address(this));
}
_executeClaim();
_getRewardPoolSlot().lastClaimTimestamp = uint32(block.timestamp);
// This only accumulates rewards claimed, it does not accumulate any secondary emissions
// that are streamed to vault users.
for (uint256 i; i < state.length; i++) {
uint256 balanceAfter = ERC20(state[i].rewardToken).balanceOf(address(this));
_accumulateSecondaryRewardViaClaim(
i,
state[i],
// balanceAfter should never be less than balanceBefore
balanceAfter - balancesBefore[i],
effectiveSupplyBefore
);
}
}
/**
* Reward Claim Methods *
*/
function _claimRewardToken(
address rewardToken,
address account,
uint256 accountSharesBefore,
uint256 accountSharesAfter,
uint256 rewardsPerVaultShare
)
internal
returns (uint256 rewardToClaim)
{
// Vault shares are always in SHARE_PRECISION
uint256 rewardDebt = _getAccountRewardDebtSlot()[rewardToken][account];
rewardToClaim = ((accountSharesBefore * rewardsPerVaultShare) / SHARE_PRECISION) - rewardDebt;
_getAccountRewardDebtSlot()[rewardToken][account] =
((accountSharesAfter * rewardsPerVaultShare) / SHARE_PRECISION);
if (0 < rewardToClaim) {
// Ignore transfer errors here so that any strange failures here do not
// prevent normal vault operations from working. Failures may include a
// lack of balances or some sort of blacklist that prevents an account
// from receiving tokens.
if (rewardToken.code.length > 0) {
try IEIP20NonStandard(rewardToken).transfer(account, rewardToClaim) {
bool success = TokenUtils.checkReturnCode();
if (success) {
emit VaultRewardTransfer(rewardToken, account, rewardToClaim);
} else {
emit VaultRewardTransfer(rewardToken, account, 0);
}
// Emits zero tokens transferred if the transfer fails.
} catch {
emit VaultRewardTransfer(rewardToken, account, 0);
}
}
}
}
/**
* ACCUMULATORS **
*/
function _accumulateSecondaryRewardViaClaim(
uint256 index,
VaultRewardState memory state,
uint256 tokensClaimed,
uint256 effectiveSupplyBefore
)
private
{
if (tokensClaimed == 0) return;
state.accumulatedRewardPerVaultShare += ((tokensClaimed * SHARE_PRECISION) / effectiveSupplyBefore).toUint128();
_getVaultRewardStateSlot()[index] = state;
}
function _accumulateSecondaryRewardViaEmissionRate(
uint256 index,
VaultRewardState memory state,
uint256 effectiveSupplyBefore
)
private
{
state.accumulatedRewardPerVaultShare =
_getAccumulatedRewardViaEmissionRate(state, effectiveSupplyBefore, block.timestamp).toUint128();
state.lastAccumulatedTime = uint32(block.timestamp);
_getVaultRewardStateSlot()[index] = state;
}
function _getAccumulatedRewardViaEmissionRate(
VaultRewardState memory state,
uint256 effectiveSupplyBefore,
uint256 blockTime
)
private
pure
returns (uint256)
{
// Short circuit the method with no emission rate
if (state.emissionRatePerYear == 0) return state.accumulatedRewardPerVaultShare;
require(0 < state.endTime);
uint256 time = blockTime < state.endTime ? blockTime : state.endTime;
uint256 additionalIncentiveAccumulatedPerVaultShare;
if (state.lastAccumulatedTime < time && VIRTUAL_SHARES < effectiveSupplyBefore) {
// NOTE: no underflow, checked in if statement
uint256 timeSinceLastAccumulation = time - state.lastAccumulatedTime;
// Precision here is:
// timeSinceLastAccumulation (SECONDS)
// emissionRatePerYear (REWARD_TOKEN_PRECISION)
// SHARE_PRECISION (1e24)
// DIVIDE BY
// YEAR (SECONDS)
// SHARE_PRECISION (1e24)
// => Precision = REWARD_TOKEN_PRECISION
additionalIncentiveAccumulatedPerVaultShare = (
timeSinceLastAccumulation * SHARE_PRECISION * state.emissionRatePerYear
) / (YEAR * effectiveSupplyBefore);
}
return state.accumulatedRewardPerVaultShare + additionalIncentiveAccumulatedPerVaultShare;
}
/// @notice Executes the proper call for various rewarder types.
function _executeClaim() internal virtual;
function _withdrawFromPreviousRewardPool(RewardPoolStorage memory oldRewardPool) internal virtual;
function _depositIntoNewRewardPool(
address poolToken,
uint256 poolTokens,
RewardPoolStorage memory newRewardPool
)
internal
virtual;
}
"
},
"src/interfaces/IRewardManager.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.8.29;
struct VaultRewardState {
address rewardToken;
uint32 lastAccumulatedTime;
uint32 endTime;
// Slot #2
// If secondary rewards are enabled, they will be streamed to the accounts via
// an annual emission rate. If the same reward token is also issued by the LP pool,
// those tokens will be added on top of the annual emission rate. If the vault is under
// automatic reinvestment mode, the secondary reward token cannot be sold.
uint128 emissionRatePerYear; // in internal token precision
uint128 accumulatedRewardPerVaultShare;
}
enum RewardPoolType {
_UNUSED,
AURA,
CONVEX_MAINNET,
CONVEX_ARBITRUM
}
struct RewardPoolStorage {
address rewardPool;
uint32 lastClaimTimestamp;
uint32 forceClaimAfter;
}
/// Each reward manager is responsible for claiming rewards for a given protocol.
/// it will be called through a delegatecall from the vault to avoid token transfers
/// of staked tokens.
interface IRewardManager {
event VaultRewardTransfer(address indexed token, address indexed account, uint256 amount);
event VaultRewardUpdate(address indexed rewardToken, uint128 emissionRatePerYear, uint32 endTime);
/// @notice Returns the current reward claim method and reward state
/// @return rewardStates Array of vault reward states
/// @return rewardPool Reward pool storage
function getRewardSettings()
external
view
returns (VaultRewardState[] memory rewardStates, RewardPoolStorage memory rewardPool);
/// @notice Returns the reward debt for the given reward token and account
/// @param rewardToken Address of the reward token
/// @param account Address of the account
/// @return rewardDebt The reward debt for the account
function getRewardDebt(address rewardToken, address account) external view returns (uint256 rewardDebt);
/// @notice Updates account rewards during enter and exit vault operations, only
/// callable via delegatecall from inside the vault
/// @param account Address of the account
/// @param effectiveSupplyBefore Total vault shares before the operation
/// @param accountSharesBefore Number of shares before the operation
/// @param accountSharesAfter Number of shares after the operation
/// @param sharesInEscrow Whether the shares are in escrow
function updateAccountRewards(
address account,
uint256 effectiveSupplyBefore,
uint256 accountSharesBefore,
uint256 accountSharesAfter,
bool sharesInEscrow
)
external
returns (uint256[] memory rewards);
/// @notice Sets a secondary reward rate for a given token, only callable via the owner
/// @param index Index of the reward token
/// @param rewardToken Address of the reward token
/// @param emissionRatePerYear Emission rate per year for the token
/// @param endTime End time for the emission rate
function updateRewardToken(
uint256 index,
address rewardToken,
uint128 emissionRatePerYear,
uint32 endTime
)
external;
/// @notice Migrates the reward pool to a new reward pool, needs to be called initially
/// to set the reward pool storage and when the reward pool is updated.
/// @param poolToken The pool token to migrate
/// @param newRewardPool The new reward pool storage configuration
function migrateRewardPool(address poolToken, RewardPoolStorage memory newRewardPool) external;
/// @notice Claims all the rewards for the entire vault and updates the accumulators
function claimRewardTokens() external;
}
"
},
"src/interfaces/Curve/IConvex.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity >=0.8.28;
interface IConvexRewardPool {
function withdrawAndUnwrap(uint256 amount, bool claim) external returns (bool);
function getReward(address _account, bool _claimExtras) external returns (bool);
function balanceOf(address _account) external view returns (uint256);
function pid() external view returns (uint256);
function operator() external view returns (address);
function extraRewards(uint256 idx) external view returns (address);
function extraRewardsLength() external view returns (uint256);
}
interface IConvexBooster {
function deposit(uint256 _pid, uint256 _amount, bool _stake) external returns (bool);
function stakerRewards() external view returns (address);
}
interface IConvexBoosterArbitrum {
function deposit(uint256 _pid, uint256 _amount) external returns (bool);
}
"
},
"node_modules/@openzeppelin/contracts/token/ERC20/ERC20.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.2.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}.
*
* All two of these 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;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view virtual returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
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;
}
/**
* @dev See {IERC20-allowance}.
*/
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);
}
}
}
}
"
},
"src/utils/TokenUtils.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.8.29;
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import { ETH_ADDRESS, ALT_ETH_ADDRESS } from "./Constants.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
library TokenUtils {
using SafeERC20 for ERC20;
function getDecimals(address token) internal view returns (uint8 decimals) {
decimals = (token == ETH_ADDRESS || token == ALT_ETH_ADDRESS) ? 18 : ERC20(token).decimals();
require(decimals <= 18);
}
function tokenBalance(address token) internal view returns (uint256) {
return token == ETH_ADDRESS ? address(this).balance : ERC20(token).balanceOf(address(this));
}
function checkApprove(ERC20 token, address spender, uint256 amount) internal {
if (address(token) == address(0)) return;
token.forceApprove(spender, amount);
}
function checkRevoke(ERC20 token, address spender) internal {
if (address(token) == address(0)) return;
token.forceApprove(spender, 0);
}
function checkReturnCode() internal pure returns (bool success) {
uint256[1] memory result;
assembly {
switch returndatasize()
case 0 {
// This is a non-standard ERC-20
success := 1 // set success to true
}
case 32 {
// This is a compliant ERC-20
returndatacopy(result, 0, 32)
success := mload(result) // Set `success = returndata` of external call
}
default {
// This is an excessively non-compliant ERC-20, revert.
revert(0, 0)
}
}
}
}
"
},
"node_modules/@openzeppelin/contracts/utils/ReentrancyGuardTransient.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/ReentrancyGuardTransient.sol)
pragma solidity ^0.8.24;
import {TransientSlot} from "./TransientSlot.sol";
/**
* @dev Variant of {ReentrancyGuard} that uses transient storage.
*
* NOTE: This variant only works on networks where EIP-1153 is available.
*
* _Available since v5.1._
*/
abstract contract ReentrancyGuardTransient {
using TransientSlot for *;
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ReentrancyGuard")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant REENTRANCY_GUARD_STORAGE =
0x9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00;
/**
* @dev Unauthorized reentrant call.
*/
error ReentrancyGuardReentrantCall();
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
// On the first call to nonReentrant, _status will be NOT_ENTERED
if (_reentrancyGuardEntered()) {
revert ReentrancyGuardReentrantCall();
}
// Any calls to nonReentrant after this point will fail
REENTRANCY_GUARD_STORAGE.asBoolean().tstore(true);
}
function _nonReentrantAfter() private {
REENTRANCY_GUARD_STORAGE.asBoolean().tstore(false);
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/
function _reentrancyGuardEntered() internal view returns (bool) {
return REENTRANCY_GUARD_STORAGE.asBoolean().tload();
}
}
"
},
"src/interfaces/IYieldStrategy.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.8.29;
import { IERC20 } from "@openzeppelin/contracts/interfaces/IERC20.sol";
import { IERC20Metadata } from "@openzeppelin/contracts/interfaces/IERC20Metadata.sol";
import { IOracle } from "./Morpho/IOracle.sol";
/**
* @notice A strategy vault that is specifically designed for leveraged yield
* strategies. Minting and burning shares are restricted to the `enterPosition`
* and `exitPosition` functions respectively. This means that shares will be
* exclusively held on lending markets as collateral unless the LendingMarket is
* set to NONE. In this case, the user will just be holding the yield token without
* any leverage.
*
* The `transfer` function is non-standard in that transfers off of a lending market
* are restricted to ensure that liquidation conditions are met.
*
* This contract also serves as its own oracle.
*/
interface IYieldStrategy is IERC20, IERC20Metadata, IOracle {
event VaultCreated(address indexed vault);
// These can be emitted by the reward manager
event VaultRewardTransfer(address indexed token, address indexed account, uint256 amount);
event VaultRewardUpdate(address indexed rewardToken, uint128 emissionRatePerYear, uint32 endTime);
// This is emitted by the trading module
event TradeExecuted(address indexed sellToken, address indexed buyToken, uint256 sellAmount, uint256 buyAmount);
event FeesCollected(uint256 feesCollected);
/**
* @dev Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing.
*
* - MUST be an ERC-20 token contract.
* - MUST NOT revert.
*/
function asset() external view returns (address assetTokenAddress);
/**
* @dev Returns the address of the accounting asset used for the
* to mark the price of the yield token excluding any market profit and loss.
* This is only used for off chain accounting.
*/
function accountingAsset() external view returns (address accountingAssetAddress);
/**
* @dev Returns the name of the strategy.
*/
function strategy() external view returns (string memory strategyName);
/**
* @dev Returns the address of the yield token held by the vault. Does not equal the share token,
* which represents each user's share of the yield tokens held by the vault.
*
* - MUST be an ERC-20 token contract.
* - MUST NOT revert.
*/
function yieldToken() external view returns (address yieldTokenAddress);
/**
* @dev Returns the total amount of the underlying asset that is “managed” by Vault.
*
* - SHOULD include any compounding that occurs from yield.
* - MUST be inclusive of any fees that are charged against assets in the Vault.
* - MUST NOT revert.
*/
function totalAssets() external view returns (uint256 totalManagedAssets);
/**
* @dev Returns the effective supply which excludes any escrowed shares.
*/
function effectiveSupply() external view returns (uint256);
/**
* @dev Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an ideal
* scenario where all the conditions are met.
*
* - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
* - MUST NOT show any variations depending on the caller.
* - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
* - MUST NOT revert.
*
* NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
* “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
* from.
*/
function convertToShares(uint256 assets) external view returns (uint256 shares);
/**
* @dev Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an ideal
* scenario where all the conditions are met.
*
* - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
* - MUST NOT show any variations depending on the caller.
* - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
*/
function convertToAssets(uint256 shares) external view returns (uint256 assets);
/**
* @dev Returns the amount of yield tokens that the Vault would exchange for the amount of shares provided, in an
* ideal
* scenario where all the conditions are met.
*/
function convertSharesToYieldToken(uint256 shares) external view returns (uint256 yieldTokens);
/**
* @dev Returns the amount of yield tokens that the account would receive for the amount of shares provided.
*/
function convertYieldTokenToShares(uint256 shares) external view returns (uint256 yieldTokens);
/**
* @dev Returns the oracle price of a yield token in terms of the asset token.
*/
function convertYieldTokenToAsset() external view returns (uint256 price);
/**
* @dev Returns the fee rate of the vault where 100% = 1e18.
*/
function feeRate() external view returns (uint256 feeRate);
/**
* @dev Returns the balance of yield tokens accrued by the vault.
*/
function feesAccrued() external view returns (uint256 feesAccruedInYieldToken);
/**
* @dev Collects the fees accrued by the vault. Only callable by the owner.
*/
function collectFees() external returns (uint256 feesCollected);
/**
* @dev Returns the price of a yield token in terms of the asset token for the
* given borrower taking into account withdrawals.
*/
function price(address borrower) external returns (uint256 price);
/**
* @notice Mints shares for a given number of assets.
*
* @param assets The amount of assets to mint shares for.
* @param receiver The address to mint the shares to.
* @param depositData calldata used to deposit the assets.
*/
function mintShares(
uint256 assets,
address receiver,
bytes memory depositData
)
external
returns (uint256 sharesMinted);
/**
* @notice Burns shares for a given number of shares.
*
* @param sharesOwner The address of the account to burn the shares for.
* @param sharesToBurn The amount of shares to burn.
* @param redeemData calldata used to redeem the yield token.
*/
function burnShares(
address sharesOwner,
uint256 sharesToBurn,
uint256 sharesHeld,
bytes memory redeemData
)
external
returns (uint256 assetsWithdrawn);
/**
* @notice Allows the lending market to transfer shares on exit position
* or liquidation.
*
* @param to The address to allow the transfer to.
* @param amount The amount of shares to allow the transfer of.
* @param currentAccount The address of the current account.
*/
function allowTransfer(address to, uint256 amount, address currentAccount) external;
/**
* @notice Pre-liquidation function.
*
* @param liquidator The address of the liquidator.
* @param liquidateAccount The address of the account to liquidate.
* @param sharesToLiquidate The amount of shares to liquidate.
* @param accountSharesHeld The amount of shares the account holds.
*/
function preLiquidation(
address liquidator,
address liquidateAccount,
uint256 sharesToLiquidate,
uint256 accountSharesHeld
)
external;
/**
* @notice Post-liquidation function.
*
* @param liquidator The address of the liquidator.
* @param liquidateAccount The address of the account to liquidate.
* @param sharesToLiquidator The amount of shares to liquidate.
*/
function postLiquidation(address liquidator, address liquidateAccount, uint256 sharesToLiquidator) external;
/**
* @notice Redeems shares for assets for a native token.
*
* @param sharesToRedeem The amount of shares to redeem.
* @param redeemData calldata used to redeem the yield token.
*/
function redeemNative(uint256 sharesToRedeem, bytes memory redeemData) external returns (uint256 assetsWithdrawn);
/**
* @notice Initiates a withdraw for a given number of shares.
*
* @param account The address of the account to initiate the withdraw for.
* @param sharesHeld The number of shares the account holds.
* @param data calldata used to initiate the withdraw.
*/
function initiateWithdraw(
address account,
uint256 sharesHeld,
bytes calldata data,
address forceWithdrawFrom
)
external
returns (uint256 requestId);
/**
* @notice Initiates a withdraw for the native balance of the account.
*
* @param data calldata used to initiate the withdraw.
*/
function initiateWithdrawNative(bytes calldata data) external returns (uint256 requestId);
/**
* @notice Clears the current account.
*/
function clearCurrentAccount() external;
}
"
},
"src/interfaces/Errors.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.8.28;
error NotAuthorized(address operator, address user);
error Unauthorized(address caller);
error UnauthorizedLendingMarketTransfer(address from, address to, uint256 value);
error InsufficientYieldTokenBalance();
error InsufficientAssetsForRepayment(uint256 assetsToRepay, uint256 assetsWithdrawn);
error CannotLiquidate(uint256 maxLiquidateShares, uint256 seizedAssets);
error CannotLiquidateZeroShares();
error Paused();
error CannotExitPositionWithinCooldownPeriod();
error CannotTokenizeWithdrawRequest();
error CurrentAccountAlreadySet();
error InvalidVault(address vault);
error WithdrawRequestNotFinalized(uint256 requestId);
error CannotInitiateWithdraw(address account);
error CannotForceWithdraw(address account);
error InsufficientSharesHeld();
error SlippageTooHigh(uint256 actualTokensOut, uint256 minTokensOut);
error CannotEnterPosition();
error NoExistingPosition();
error LiquidatorHasPosition();
error InvalidUpgrade();
error InvalidInitialization();
error InvalidLendingRouter();
error ExistingWithdrawRequest(address vault, address account, uint256 requestId);
error NoWithdrawRequest(address vault, address account);
error InvalidWithdrawRequestTokenization();
error InvalidPrice(uint256 oraclePrice, uint256 spotPrice);
error PoolShareTooHigh(uint256 poolClaim, uint256 maxSupplyThreshold);
error AssetRemaining(uint256 assetRemaining);
"
},
"src/utils/Constants.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.8.29;
import { WETH9 } from "../interfaces/IWETH.sol";
import { AddressRegistry } from "../proxy/AddressRegistry.sol";
address constant ETH_ADDRESS = address(0);
address constant ALT_ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
uint256 constant DEFAULT_PRECISION = 1e18;
uint256 constant DEFAULT_DECIMALS = 18;
uint256 constant SHARE_PRECISION = 1e24;
uint256 constant VIRTUAL_SHARES = 1e6;
uint256 constant COOLDOWN_PERIOD = 5 minutes;
uint256 constant YEAR = 365 days;
// Will move these to a deployment file when we go to multiple chains
uint256 constant CHAIN_ID_MAINNET = 1;
WETH9 constant WETH = WETH9(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);
AddressRegistry constant ADDRESS_REGISTRY = AddressRegistry(0xe335d314BD4eF7DD44F103dC124FEFb7Ce63eC95);
"
},
"src/utils/TypeConvert.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.8.29;
library TypeConvert {
function toUint(int256 x) internal pure returns (uint256) {
require(x >= 0);
return uint256(x);
}
function toInt(uint256 x) internal pure returns (int256) {
require(x <= uint256(type(int256).max)); // dev: toInt overflow
return int256(x);
}
function toUint128(uint256 x) internal pure returns (uint128) {
require(x <= uint128(type(uint128).max)); // dev: toUint128 overflow
return uint128(x);
}
function toUint120(uint256 x) internal pure returns (uint120) {
require(x <= uint120(type(uint120).max)); // dev: toUint120 overflow
return uint120(x);
}
}
"
},
"src/interfaces/IEIP20NonStandard.sol": {
"content": "// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.8.28;
/**
* @title EIP20NonStandardInterface
* @dev Version of ERC20 with no return values for `transfer` and `transferFrom`
* See https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca
*/
interface IEIP20NonStandard {
/**
* @notice Get the total number of tokens in circulation
* @return The supply of tokens
*/
function totalSupply() external view returns (uint256);
/**
* @notice Gets the balance of the specified address
* @param owner The address from which the balance will be retrieved
* @return balance
*/
function balanceOf(address owner) external view returns (uint256 balance);
///
/// !!!!!!!!!!!!!!
/// !!! NOTICE !!! `transfer` does not return a value, in violation of the ERC-20 specification
/// !!!!!!!!!!!!!!
///
/**
* @notice Transfer `amount` tokens from `msg.sender` to `dst`
* @param dst The address of the destination account
* @param amount The number of tokens to transfer
*/
function transfer(address dst, uint256 amount) external;
///
/// !!!!!!!!!!!!!!
/// !!! NOTICE !!! `transferFrom` does not return a value, in violation of the ERC-20 specification
/// !!!!!!!!!!!!!!
///
/**
* @notice Transfer `amount` tokens from `src` to `dst`
* @param src The address of the source account
* @param dst The address of the destination account
* @param amount The number of tokens to transfer
*/
function transferFrom(address src, address dst, uint256 amount) external;
///
/// !!!!!!!!!!!!!!
/// !!! NOTICE !!! `approve` does not return a value, in violation of the ERC-20 specification
/// !!!!!!!!!!!!!!
///
/**
* @notice Approve `spender` to transfer up to `amount` from `src`
* @dev This will overwrite the approval amount for `spender`
* and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve)
* @param spender The address of the account which may transfer tokens
* @param amount The number of tokens that are approved
*/
function approve(address spender, uint256 amount) external;
/**
* @notice Get the current allowance from `owner` for `spender`
* @param owner The address of the account which owns the tokens to be spent
* @param spender The address of the account which may transfer tokens
* @return remaining The number of tokens allowed to be spent
*/
function allowance(address owner, address spender) external view returns (uint256 remaining);
event Transfer(address indexed from, address indexed to, uint256 amount);
event Approval(address indexed owner, address indexed spender, uint256 amount);
}
"
},
"node_modules/@openzeppelin/contracts/token/ERC20/IERC20.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC-20 standard as defined in the ERC.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
}
"
},
"node_modules/@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC-20 standard.
*/
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);
}
"
},
"node_modules/@openzeppelin/contracts/utils/Context.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}
"
},
"node_modules/@openzeppelin/contracts/interfaces/draft-IERC6093.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (interfaces/draft-IERC6093.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard ERC-20 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-20 tokens.
*/
interface IERC20Errors {
/**
* @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param balance Current balance for the interacting account.
* @param needed Minimum amount required to perform a transfer.
*/
error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC20InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC20InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.
* @param spender Address that may be allowed to operate on tokens without being their owner.
* @param allowance Amount of tokens a `spender` is allowed to operate with.
* @param needed Minimum amount required to perform a transfer.
*/
error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC20InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `spender` to be approved. Used in approvals.
* @param spender Address that may be allowed to operate on tokens without being their owner.
*/
error ERC20InvalidSpender(address spender);
}
/**
* @dev Standard ERC-721 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-721 tokens.
*/
interface IERC721Errors {
/**
* @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in ERC-20.
* Used in balance queries.
* @param owner Address of the current owner of a token.
*/
error ERC721InvalidOwner(address owner);
/**
* @dev Indicates a `tokenId` whose `owner` is the zero address.
* @param tokenId Identifier number of a token.
*/
error ERC721NonexistentToken(uint256 tokenId);
/**
* @dev Indicates an error related to the ownership over a particular token. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param tokenId Identifier number of a token.
* @param owner Address of the current owner of a token.
*/
error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC721InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC721InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `operator`’s approval. Used in transfers.
* @param operator Address that may be allowed to operate on tokens without being their owner.
* @param tokenId Identifier number of a token.
*/
error ERC721InsufficientApproval(address operator, uint256 tokenId);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC721InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `operator` to be approved. Used in approvals.
* @param operator Address that may be allowed to operate on tokens without being their owner.
*/
error ERC721InvalidOperator(address operator);
}
/**
* @dev Standard ERC-1155 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-1155 tokens.
*/
interface IERC1155Errors {
/**
* @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param balance Current balance for the interacting account.
* @param needed Minimum amount required to perform a transfer.
* @param tokenId Identifier number of a token.
*/
error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC1155InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC1155InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `operator`’s approval. Used in transfers.
* @param operator Address that may be allowed to operate on tokens without being their owner.
* @param owner Address of the current owner of a token.
*/
error ERC1155MissingApprovalForAll(address operator, address owner);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC1155InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `operator` to be approved. Used in approvals.
* @param operator Address that may be allowed to operate on tokens without being their owner.
*/
error ERC1155InvalidOperator(address operator);
/**
* @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.
* Used in batch transfers.
* @param idsLength Length of the array of token identifiers
* @param valuesLength Length of the array of token amounts
*/
error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
}
"
},
"node_modules/@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.2.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC1363} from "../../../interfaces/IERC1363.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC-20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and
Submitted on: 2025-09-30 10:08:22
Comments
Log in to comment.
No comments yet.