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/periphery/strategies/MultisigStrategy.sol": {
"content": "// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;
import {BaseStrategy} from "./BaseStrategy.sol";
import {BaseStrategyStorageLib as BaseStrategyStorage} from "../lib/BaseStrategyStorageLib.sol";
import {MultisigStrategyStorageLib as MultisigStrategyStorage} from "../lib/MultisigStrategyStorageLib.sol";
import {PositionAccountingStorageLib} from "../lib/PositionAccountingStorageLib.sol";
import {PositionAccountingLib} from "../lib/PositionAccountingLib.sol";
import {PeripheryRolesLib} from "../lib/PeripheryRolesLib.sol";
import {StrategyType} from "../../interface/IStrategyTemplate.sol";
import {IERC20} from "@openzeppelin-contracts/token/ERC20/IERC20.sol";
import {IERC4626} from "@openzeppelin-contracts/interfaces/IERC4626.sol";
import {SafeERC20} from "@openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";
import {Math} from "@openzeppelin-contracts/utils/math/Math.sol";
import {SafeCast} from "@openzeppelin-contracts/utils/math/SafeCast.sol";
import {SignedMath} from "@openzeppelin-contracts/utils/math/SignedMath.sol";
/**
* @title MultisigStrategy
* @dev A strategy that forwards assets to a designated multi-signature wallet
* @dev Implements BaseStrategy for integration with the vault system
* @dev This strategy simply forwards deposits to a multi-sig wallet and retrieves
* them on withdrawal. It does not generate any rewards.
* @dev Uses EIP-7201 storage layout for upgradeability.
*/
contract MultisigStrategy is BaseStrategy {
using SafeERC20 for IERC20;
using Math for uint256;
using SafeCast for int256;
using SignedMath for int256;
/// @notice Emitted when the multi-sig is set
/// @param multiSig The address of the multi-sig wallet
/// @param newMultiSig The new address of the multi-sig wallet
event MultiSigSet(address indexed multiSig, address indexed newMultiSig);
/// @notice Emitted when assets are forwarded to the multi-sig
/// @param multiSig The address of the multi-sig wallet
/// @param asset The address of the asset forwarded
/// @param amount The amount of assets forwarded
event AssetsForwarded(address indexed multiSig, address asset, uint256 amount);
/// @notice Emitted when assets are retrieved from the multi-sig
/// @param multiSig The address of the multi-sig wallet
/// @param asset The address of the asset retrieved
/// @param amount The amount of assets retrieved
event AssetsRetrieved(address indexed multiSig, address asset, uint256 amount);
/// @notice Emitted when the total assets are adjusted
/// @param accountingNonce The accounting nonce
/// @param totalAssets The new total assets
/// @param diff The amount of underlying assets to adjust the total assets by
event AdjustTotalAssets(uint256 accountingNonce, uint256 totalAssets, int256 diff);
/// @notice Custom errors
error InvalidMultiSigAddress();
error InsufficientUnderlyingBalance();
error NotAdminOrOperator();
/// @notice Modifier to ensure only admin or operator can call functions
modifier onlyAdminOrOperator() {
if (
!hasRole(PeripheryRolesLib.STRATEGY_ADMIN, msg.sender)
&& !hasRole(PeripheryRolesLib.OPERATOR_ROLE, msg.sender)
) {
revert NotAdminOrOperator();
}
_;
}
/**
* @dev Constructor that disables initializers
*/
constructor() {
_disableInitializers();
}
/**
* @dev Initializer function
* @param admin The address that will have admin role
* @param vault_ The address of the authorized vault
* @param multiSig_ The address of the multi-signature wallet
* @param maxAccountingChangeThreshold_ The maximum accounting change threshold in basis points
* @param accountingValidityPeriod_ The accounting validity period in seconds
* @param cooldownPeriod_ The update cooldown period in seconds
*/
function initialize(
address admin,
address vault_,
address multiSig_,
uint64 maxAccountingChangeThreshold_,
uint64 accountingValidityPeriod_,
uint64 cooldownPeriod_
) external initializer {
if (multiSig_ == address(0)) revert InvalidMultiSigAddress();
if (cooldownPeriod_ >= accountingValidityPeriod_) {
revert PositionAccountingLib.InvalidCooldownPeriod();
}
if (maxAccountingChangeThreshold_ > PositionAccountingLib.BASIS_POINTS) {
revert PositionAccountingLib.InvalidMaxAccountingChangeThreshold();
}
// Initialize BaseStrategy first
_initializeBaseStrategy(admin, vault_);
// Initialize MultisigStrategyStorage
MultisigStrategyStorage.initialize(multiSig_);
// Initialize PositionAccountingStorage
PositionAccountingStorageLib.initialize(
cooldownPeriod_, maxAccountingChangeThreshold_, accountingValidityPeriod_
);
}
/**
* GETTER FUNCTIONS
*/
/**
* @notice Returns the address of the multi-signature wallet
* @return The address of the multi-signature wallet
*/
function getMultiSig() external view returns (address) {
MultisigStrategyStorage.MultisigStrategyStorage storage multisigStrategyStorage =
MultisigStrategyStorage.fetch();
return multisigStrategyStorage.multiSig;
}
/**
* @notice Returns the next accounting nonce
* @return The next accounting nonce
*/
function getNextAccountingNonce() public view returns (uint256) {
return PositionAccountingLib.getNextAccountingNonce();
}
/**
* @notice Returns whether an address has the operator role
* @param account The address to check
* @return True if the address has the operator role
*/
function isOperator(address account) external view returns (bool) {
return hasRole(PeripheryRolesLib.OPERATOR_ROLE, account);
}
/**
* @notice Returns the last updated timestamp
* @return The last updated timestamp
*/
function getLastUpdatedTimestamp() external view returns (uint64) {
return PositionAccountingLib.getLastUpdatedTimestamp();
}
/**
* @notice Returns the max accounting change threshold
* @return The max accounting change threshold
*/
function getMaxAccountingChangeThreshold() external view returns (uint64) {
return PositionAccountingLib.getMaxAccountingChangeThreshold();
}
/**
* @notice Returns the accounting validity period
* @return The accounting validity period
*/
function getAccountingValidityPeriod() external view returns (uint64) {
return PositionAccountingLib.getAccountingValidityPeriod();
}
/**
* @notice Returns the update cooldown period
* @return The update cooldown period
*/
function getCooldownPeriod() external view returns (uint64) {
return PositionAccountingLib.getCooldownPeriod();
}
/**
* @dev Override strategyType for MultisigStrategy
* @dev MultisigStrategy is an async strategy that requires multisig approval for operations
* @return ASYNC strategy type
*/
function strategyType() external pure override returns (StrategyType) {
return StrategyType.ASYNC;
}
/**
* SETTER FUNCTIONS
*/
/**
* @notice Sets the address of the multi-signature wallet
* @param multiSig_ The address of the multi-signature wallet
* @dev The multiSig must approve this contract to pull funds for withdrawals
*/
function setMultiSig(address multiSig_) external onlyRole(PeripheryRolesLib.STRATEGY_ADMIN) {
if (multiSig_ == address(0)) revert InvalidMultiSigAddress();
MultisigStrategyStorage.MultisigStrategyStorage storage multisigStrategyStorage =
MultisigStrategyStorage.fetch();
emit MultiSigSet(multisigStrategyStorage.multiSig, multiSig_);
multisigStrategyStorage.multiSig = multiSig_;
}
/**
* @notice Sets the max accounting change threshold
* @param maxAccountingChangeThreshold_ The maximum accounting change threshold in basis points (10000 = 100%, 100 = 1%)
*/
function setMaxAccountingChangeThreshold(uint64 maxAccountingChangeThreshold_)
external
onlyRole(PeripheryRolesLib.STRATEGY_ADMIN)
{
PositionAccountingLib.setMaxAccountingChangeThreshold(maxAccountingChangeThreshold_);
}
/**
* @notice Sets the accounting change validation period
* @param accountingValidityPeriod_ The new accounting change validation period in seconds
*/
function setAccountingValidityPeriod(uint64 accountingValidityPeriod_)
external
onlyRole(PeripheryRolesLib.STRATEGY_ADMIN)
{
PositionAccountingLib.setAccountingValidityPeriod(accountingValidityPeriod_);
}
/**
* @notice Sets the accounting update cooldown period
* @param cooldownPeriod_ The new update cooldown period in seconds
*/
function setCooldownPeriod(uint64 cooldownPeriod_) external onlyRole(PeripheryRolesLib.STRATEGY_ADMIN) {
PositionAccountingLib.setCooldownPeriod(cooldownPeriod_);
}
/**
* ADMIN STATE MODIFYING FUNCTIONS
*/
/**
* @notice Unpauses the strategy and adjusts the total assets
* @dev Can only be called by the owner
* @dev Skips the validation of the accounting change, used by the admin multisig to fix the accounting when the strategy is paused
* @dev Allows the admin to correct the accounting when the strategy is paused in a single transaction even if the change exceeds the max accounting change threshold
* @param diff The amount of underlying assets to adjust the total assets by
*/
function unpauseAndAdjustTotalAssets(int256 diff) external onlyRole(PeripheryRolesLib.STRATEGY_ADMIN) {
_unpause();
_adjustTotalAssets(diff);
}
/**
* @notice Handles the accounting for the amount of underlying assets owned by the multisig wallet and by its positions.
* @param diff The amount of underlying assets to adjust the total assets by
*/
function adjustTotalAssets(int256 diff, uint256 accountingNonce_) external whenNotPaused onlyAdminOrOperator {
MultisigStrategyStorage.MultisigStrategyStorage storage multisigStrategyStorage =
MultisigStrategyStorage.fetch();
// Validate the accounting change
if (
!PositionAccountingLib.isValidAccountingChange(
diff, accountingNonce_, multisigStrategyStorage.vaultDepositedAmount
)
) {
_pause();
return;
}
// Adjust the total assets
_adjustTotalAssets(diff);
}
/**
* INTERNAL FUNCTIONS
*/
/**
* @dev function to preview the current position value
* @return The current vault deposited amount
*/
function _previewPosition() internal view override returns (uint256) {
// Check accounting validity period
PositionAccountingLib._checkAccountingValidity();
MultisigStrategyStorage.MultisigStrategyStorage storage multisigStrategyStorage =
MultisigStrategyStorage.fetch();
return multisigStrategyStorage.vaultDepositedAmount;
}
/**
* @dev function to allocate funds to the position
* @param data The data containing the amount to allocate
* @return The actual amount allocated
*/
function _allocateToPosition(bytes calldata data) internal override whenNotPaused returns (uint256) {
uint256 amount;
assembly {
amount := calldataload(data.offset)
}
PositionAccountingLib._checkAccountingValidity();
MultisigStrategyStorage.MultisigStrategyStorage storage multisigStrategyStorage =
MultisigStrategyStorage.fetch();
multisigStrategyStorage.vaultDepositedAmount += amount;
BaseStrategyStorage.BaseStrategyStorage storage baseStrategyStorage = BaseStrategyStorage.fetch();
IERC20(baseStrategyStorage.asset).safeTransfer(multisigStrategyStorage.multiSig, amount);
emit AssetsForwarded(multisigStrategyStorage.multiSig, baseStrategyStorage.asset, amount);
return amount;
}
/**
* @dev Internal function to retrieve assets from the multisig
* @param amount The amount of assets to retrieve
* @return The actual amount retrieved
*/
function _retrieveAssetsFromMultisig(uint256 amount) internal whenNotPaused returns (uint256) {
PositionAccountingLib._checkAccountingValidity();
MultisigStrategyStorage.MultisigStrategyStorage storage multisigStrategyStorage =
MultisigStrategyStorage.fetch();
if (amount > multisigStrategyStorage.vaultDepositedAmount) revert InsufficientUnderlyingBalance();
multisigStrategyStorage.vaultDepositedAmount -= amount;
BaseStrategyStorage.BaseStrategyStorage storage baseStrategyStorage = BaseStrategyStorage.fetch();
IERC20(baseStrategyStorage.asset).safeTransferFrom(multisigStrategyStorage.multiSig, address(this), amount);
emit AssetsRetrieved(multisigStrategyStorage.multiSig, baseStrategyStorage.asset, amount);
return amount;
}
/**
* @dev function to deallocate funds from the position
* @param data The data containing the amount to deallocate
* @return The actual amount deallocated
*/
function _deallocateFromPosition(bytes calldata data) internal override returns (uint256) {
uint256 amount;
assembly {
amount := calldataload(data.offset)
}
return _retrieveAssetsFromMultisig(amount);
}
/**
* @dev function to withdraw funds from the position
* @param assets The amount of assets to withdraw
* @return The actual amount withdrawn
*/
function _withdrawFromPosition(uint256 assets) internal override returns (uint256) {
return _retrieveAssetsFromMultisig(assets);
}
/**
* @notice Internal function to adjust total assets
* @param diff The amount of underlying assets to adjust the total assets by
*/
function _adjustTotalAssets(int256 diff) internal {
MultisigStrategyStorage.MultisigStrategyStorage storage multisigStrategyStorage =
MultisigStrategyStorage.fetch();
// Update timestamp and nonce
uint256 newNonce = PositionAccountingLib.updateTimestampAndNonce();
if (diff < 0) {
uint256 absDiff = uint256(-diff);
if (absDiff > multisigStrategyStorage.vaultDepositedAmount) revert InsufficientUnderlyingBalance();
multisigStrategyStorage.vaultDepositedAmount -= absDiff;
} else {
multisigStrategyStorage.vaultDepositedAmount += uint256(diff);
}
emit AdjustTotalAssets(newNonce, multisigStrategyStorage.vaultDepositedAmount, diff);
}
}
"
},
"src/periphery/strategies/BaseStrategy.sol": {
"content": "// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;
import {IBaseStrategy} from "../interface/IBaseStrategy.sol";
import {IStrategyTemplate, StrategyType} from "../../interface/IStrategyTemplate.sol";
import {IERC20} from "@openzeppelin-contracts/token/ERC20/IERC20.sol";
import {IERC4626} from "@openzeppelin-contracts/interfaces/IERC4626.sol";
import {SafeERC20} from "@openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";
import {AccessControlUpgradeable} from "@openzeppelin-upgradeable/access/AccessControlUpgradeable.sol";
import {PausableUpgradeable} from "@openzeppelin-upgradeable/utils/PausableUpgradeable.sol";
import {Initializable} from "@openzeppelin-upgradeable/proxy/utils/Initializable.sol";
import {BaseStrategyStorageLib as BaseStrategyStorage} from "../lib/BaseStrategyStorageLib.sol";
import {PeripheryRolesLib} from "../lib/PeripheryRolesLib.sol";
/**
* @title BaseStrategy
* @dev Abstract base strategy implementation that provides common functionality
* @dev for all strategy implementations including yield accrual and position management.
* @dev Uses EIP-7201 storage layout for upgradeability.
*/
abstract contract BaseStrategy is IBaseStrategy, AccessControlUpgradeable, PausableUpgradeable {
using SafeERC20 for IERC20;
/**
* @dev Constructor that disables initializers
*/
constructor() {
_disableInitializers();
}
/**
* @dev External initializer function
* @param admin The address that will have admin role
* @param vault_ The address of the authorized vault
*/
function initialize(address admin, address vault_) external initializer {
_initializeBaseStrategy(admin, vault_);
}
/**
* @dev Internal initializer function that can be called by child contracts
* @param admin The address that will have admin role
* @param vault_ The address of the authorized vault
*/
function _initializeBaseStrategy(address admin, address vault_) internal virtual {
if (admin == address(0)) revert ZeroAdminAddress();
if (vault_ == address(0)) revert ZeroVaultAddress();
__AccessControl_init();
__Pausable_init();
_setRoleAdmin(PeripheryRolesLib.STRATEGY_ADMIN, PeripheryRolesLib.STRATEGY_ADMIN_ADMIN);
_setRoleAdmin(PeripheryRolesLib.OPERATOR_ROLE, PeripheryRolesLib.OPERATOR_ADMIN);
_grantRole(PeripheryRolesLib.STRATEGY_ADMIN_ADMIN, admin);
_grantRole(PeripheryRolesLib.OPERATOR_ADMIN, admin);
_grantRole(PeripheryRolesLib.OPERATOR_ROLE, admin);
_grantRole(PeripheryRolesLib.STRATEGY_ADMIN, admin);
BaseStrategyStorage.BaseStrategyStorage storage baseStrategyStorage = BaseStrategyStorage.fetch();
baseStrategyStorage.vault = vault_;
baseStrategyStorage.asset = IERC4626(vault_).asset();
// Default to unlimited withdrawals
baseStrategyStorage.maxWithdraw = type(uint256).max;
}
/**
* @dev Modifier to ensure only the authorized vault can call functions
*/
modifier onlyVault() {
BaseStrategyStorage.BaseStrategyStorage storage baseStrategyStorage = BaseStrategyStorage.fetch();
if (msg.sender != baseStrategyStorage.vault) revert UnauthorizedVault();
_;
}
/**
* @dev Abstract virtual function to preview the current position value
* @dev Must be implemented by derived strategies to return the current position value
* @return The current position value
*/
function _previewPosition() internal view virtual returns (uint256);
/**
* @dev Abstract virtual function to allocate funds to the position
* @param data The data containing the amount to allocate and protocol specific data
* @return The actual amount allocated
*/
function _allocateToPosition(bytes calldata data) internal virtual returns (uint256);
/**
* @dev Abstract virtual function to deallocate funds from the position
* @param data The data containing the amount to deallocate and protocol specific data
* @return The actual amount deallocated
*/
function _deallocateFromPosition(bytes calldata data) internal virtual returns (uint256);
/**
* @dev Abstract virtual function to withdraw funds from the position
* @param assets The amount of assets to withdraw
* @return The actual amount withdrawn
*/
function _withdrawFromPosition(uint256 assets) internal virtual returns (uint256);
/**
* @inheritdoc IStrategyTemplate
*/
function allocateFunds(bytes calldata data) external virtual override onlyVault whenNotPaused returns (uint256) {
uint256 amountToAllocate;
assembly {
amountToAllocate := calldataload(data.offset)
}
if (amountToAllocate == 0) return 0;
// Cache storage reference to avoid multiple sloads
BaseStrategyStorage.BaseStrategyStorage storage baseStrategyStorage = BaseStrategyStorage.fetch();
address vault = baseStrategyStorage.vault;
IERC20(baseStrategyStorage.asset).safeTransferFrom(vault, address(this), amountToAllocate);
uint256 actualAllocated = _allocateToPosition(data);
emit AllocateFunds(actualAllocated);
return actualAllocated;
}
/**
* @inheritdoc IStrategyTemplate
*/
function deallocateFunds(bytes calldata data) external virtual override onlyVault whenNotPaused returns (uint256) {
uint256 amountToDeallocate;
assembly {
amountToDeallocate := calldataload(data.offset)
}
if (amountToDeallocate == 0) return 0;
uint256 actualDeallocated = _deallocateFromPosition(data);
BaseStrategyStorage.BaseStrategyStorage storage baseStrategyStorage = BaseStrategyStorage.fetch();
IERC20(baseStrategyStorage.asset).safeTransfer(baseStrategyStorage.vault, actualDeallocated);
emit DeallocateFunds(actualDeallocated);
return actualDeallocated;
}
/**
* @inheritdoc IStrategyTemplate
*/
function onWithdraw(uint256 assets) external virtual override onlyVault whenNotPaused returns (uint256) {
// Check maxWithdraw limit
uint256 maxWithdrawAmount = this.maxWithdraw();
if (assets > maxWithdrawAmount) revert MaxWithdrawAmountExceeded();
uint256 actualWithdrawn = _withdrawFromPosition(assets);
BaseStrategyStorage.BaseStrategyStorage storage baseStrategyStorage = BaseStrategyStorage.fetch();
IERC20(baseStrategyStorage.asset).safeTransfer(baseStrategyStorage.vault, actualWithdrawn);
emit StrategyWithdraw(actualWithdrawn);
return actualWithdrawn;
}
/**
* @inheritdoc IStrategyTemplate
*/
function asset() external view virtual override returns (address) {
return BaseStrategyStorage.fetch().asset;
}
/**
* @inheritdoc IStrategyTemplate
*/
function totalAllocatedValue() external view virtual override whenNotPaused returns (uint256) {
return _previewPosition();
}
/**
* @inheritdoc IStrategyTemplate
*/
function maxAllocation() external pure virtual override returns (uint256) {
return type(uint256).max;
}
/**
* @inheritdoc IStrategyTemplate
*/
function maxWithdraw() external view virtual override returns (uint256) {
BaseStrategyStorage.BaseStrategyStorage storage baseStrategyStorage = BaseStrategyStorage.fetch();
uint256 positionValue = _previewPosition();
return baseStrategyStorage.maxWithdraw > positionValue ? positionValue : baseStrategyStorage.maxWithdraw;
}
/**
* @dev Get the authorized vault address
* @return The authorized vault address
*/
function getVault() external view virtual returns (address) {
return BaseStrategyStorage.fetch().vault;
}
/**
* @dev Set the maximum withdraw amount for the strategy
* @param maxWithdraw_ The maximum amount that can be withdrawn
*/
function setMaxWithdraw(uint256 maxWithdraw_) external virtual onlyRole(PeripheryRolesLib.STRATEGY_ADMIN) {
BaseStrategyStorage.BaseStrategyStorage storage baseStrategyStorage = BaseStrategyStorage.fetch();
emit MaxWithdrawUpdated(baseStrategyStorage.maxWithdraw, maxWithdraw_);
baseStrategyStorage.maxWithdraw = maxWithdraw_;
}
/**
* @dev Rescue function to recover tokens (only admin)
* @dev Cannot rescue the strategy's asset token
* @param token The token address to rescue
* @param amount The amount to rescue (0 to rescue all available tokens)
*/
function rescueToken(address token, uint256 amount) external virtual onlyRole(PeripheryRolesLib.STRATEGY_ADMIN) {
BaseStrategyStorage.BaseStrategyStorage storage baseStrategyStorage = BaseStrategyStorage.fetch();
if (token == baseStrategyStorage.asset) revert InvalidAsset();
uint256 rescueAmount = amount == 0 ? IERC20(token).balanceOf(address(this)) : amount;
IERC20(token).safeTransfer(msg.sender, rescueAmount);
}
/**
* @notice Pauses the strategy, preventing deposits and withdrawals
* @dev Only admin can pause the strategy
*/
function pause() external onlyRole(PeripheryRolesLib.STRATEGY_ADMIN) {
_pause();
}
/**
* @notice Unpauses the strategy, allowing deposits and withdrawals
* @dev Only admin can unpause the strategy
*/
function unpause() external onlyRole(PeripheryRolesLib.STRATEGY_ADMIN) {
_unpause();
}
}
"
},
"src/periphery/lib/BaseStrategyStorageLib.sol": {
"content": "// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;
library BaseStrategyStorageLib {
/// @dev keccak256(abi.encode(uint256(keccak256("concrete.storage.BaseStrategyStorage")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant BaseStrategyStorageLocation =
0xe84a5801edbad7de8e77ad0d2d730a53019bf3035b3c2f0ee45940fd7a547900;
/// @custom:storage-location erc7201:concrete.storage.BaseStrategyStorage
struct BaseStrategyStorage {
/// @dev The underlying asset token
address asset;
/// @dev The single authorized vault address
address vault;
/// @dev The maximum amount that can be withdrawn from the strategy
uint256 maxWithdraw;
}
/**
* @dev Get the storage struct
*/
function fetch() internal pure returns (BaseStrategyStorage storage $) {
assembly {
$.slot := BaseStrategyStorageLocation
}
}
}
"
},
"src/periphery/lib/MultisigStrategyStorageLib.sol": {
"content": "// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;
/**
* @title MultisigStrategyStorageLib
* @dev Library for managing MultisigStrategy-specific storage
* @dev Provides storage for multisig address, deposited amount, withdraw state, and operator
*/
library MultisigStrategyStorageLib {
/// @dev Storage location for MultisigStrategyStorage keccak256(abi.encode(uint256(keccak256("concrete.storage.MultisigStrategyStorage")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant MULTISIG_STRATEGY_STORAGE_LOCATION =
0xcb7da0d8897752a6df968d7ec6cb8f24f19b693fff4548ee51892258c2c21a00;
/// @notice The MultisigStrategy storage structure
struct MultisigStrategyStorage {
address multiSig; // The address of the multi-signature wallet
uint256 vaultDepositedAmount; // The amount of assets deposited into this strategy
}
/// @notice Fetches the MultisigStrategyStorage from the specified location
/// @return $ The MultisigStrategyStorage struct
function fetch() internal pure returns (MultisigStrategyStorage storage $) {
bytes32 position = MULTISIG_STRATEGY_STORAGE_LOCATION;
assembly {
$.slot := position
}
}
/// @notice Initializes the MultisigStrategyStorage
/// @param multiSig_ The address of the multi-signature wallet
function initialize(address multiSig_) internal {
MultisigStrategyStorage storage $ = fetch();
$.multiSig = multiSig_;
}
}
"
},
"src/periphery/lib/PositionAccountingStorageLib.sol": {
"content": "// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;
/**
* @title PositionAccountingStorageLib
* @dev Library for managing position accounting storage and validation
* @dev Provides functionality for accounting change validation, cooldown periods, and thresholds
*/
library PositionAccountingStorageLib {
/// @dev Storage location for keccak256(abi.encode(uint256(keccak256("concrete.storage.Positionaccounting.")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant POSITION_ACCOUNTING_STORAGE_LOCATION =
0x5ce5a25f3602968dae3457825179f308a81a0ae9fafb34e4d83f623ffdb37f00;
/// @notice The position accounting configuration structure
struct PositionAccountingStorage {
uint64 lastUpdatedTimestamp;
uint64 cooldownPeriod; // in seconds
uint64 maxAccountingChangeThreshold; // in basis points (10000 = 100%, 10 = 0.1%)
uint64 accountingValidityPeriod; // in seconds
uint256 accountingNonce;
}
/// @notice Fetches the PositionAccountingStorage from the specified location
/// @return $ The PositionAccountingStorage struct
function fetch() internal pure returns (PositionAccountingStorage storage $) {
bytes32 position = POSITION_ACCOUNTING_STORAGE_LOCATION;
assembly {
$.slot := position
}
}
/// @notice Initializes the PositionAccountingStorage with default values
/// @param cooldownPeriod_ The update cooldown period in seconds
/// @param maxAccountingChangeThreshold_ The maximum accounting change threshold in basis points
/// @param accountingValidityPeriod_ The accounting validity period in seconds
function initialize(uint64 cooldownPeriod_, uint64 maxAccountingChangeThreshold_, uint64 accountingValidityPeriod_)
internal
{
PositionAccountingStorage storage $ = fetch();
$.lastUpdatedTimestamp = uint64(block.timestamp);
$.cooldownPeriod = cooldownPeriod_;
$.maxAccountingChangeThreshold = maxAccountingChangeThreshold_;
$.accountingValidityPeriod = accountingValidityPeriod_;
$.accountingNonce = 0;
}
}
"
},
"src/periphery/lib/PositionAccountingLib.sol": {
"content": "// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;
import {PositionAccountingStorageLib} from "./PositionAccountingStorageLib.sol";
import {SafeCast} from "@openzeppelin-contracts/utils/math/SafeCast.sol";
/**
* @title PositionAccountingLib
* @dev Library for managing position accounting operations and validation
* @dev Provides functionality for accounting change validation, cooldown periods, and thresholds
*/
library PositionAccountingLib {
using SafeCast for int256;
/// @notice Basis points constant (10000 = 100%)
uint256 public constant BASIS_POINTS = 10000;
/// @notice Custom errors
error InvalidMaxAccountingChangeThreshold();
/// @notice Minimum difference between cooldown period and accounting validity period (60 seconds)
uint64 private constant MIN_PERIOD_DIFFERENCE = 60;
/// @notice Emitted when an accounting change is too large
/// @param accountingNonce The accounting nonce of the accounting change
/// @param diff yield(+) or loss(-) amount
/// @param oldAccounting The accounting before the accounting change
/// @param maxAccountingChangeThreshold The maximum allowed accounting change threshold (BASIS_POINTS = 100%, 100 = 1%)
event AccountingChangeTooLarge(
uint256 accountingNonce, int256 diff, uint256 oldAccounting, uint64 maxAccountingChangeThreshold
);
/// @notice Emitted when the update cooldown period is not passed
/// @param accountingNonce The accounting nonce of the accounting change
/// @param currentTimestamp The current timestamp
/// @param coolDownTimestamp The cool down timestamp
event CooldownPeriodNotPassed(uint256 accountingNonce, uint256 currentTimestamp, uint256 coolDownTimestamp);
/// @notice Emitted when the max accounting change threshold is set
/// @param oldMaxAccountingChangeThreshold The old max accounting change threshold
/// @param maxAccountingChangeThreshold The new max accounting change threshold
event MaxAccountingChangeThresholdSet(uint64 oldMaxAccountingChangeThreshold, uint64 maxAccountingChangeThreshold);
/// @notice Emitted when the update cooldown period is set
/// @param oldCooldownPeriod The old update cooldown period
/// @param cooldownPeriod The new update cooldown period
event SetCooldownPeriod(uint64 oldCooldownPeriod, uint64 cooldownPeriod);
/// @notice Emitted when the accounting validity period is set
/// @param oldAccountingValidityPeriod The old accounting validity period
/// @param accountingValidityPeriod The new accounting validity period
event SetAccountingValidityPeriod(uint64 oldAccountingValidityPeriod, uint64 accountingValidityPeriod);
/// @notice Custom errors
error InvalidAccountingNonce(uint256 provided, uint256 expected);
error InsufficientUnderlyingBalance();
error AccountingValidityPeriodExpired();
error InvalidAccountingValidityPeriod();
error InvalidCooldownPeriod();
/**
* @notice Sets the max accounting change threshold
* @param maxAccountingChangeThreshold_ The maximum accounting change threshold in basis points (BASIS_POINTS = 100%, 100 = 1%)
*/
function setMaxAccountingChangeThreshold(uint64 maxAccountingChangeThreshold_) internal {
require(maxAccountingChangeThreshold_ <= BASIS_POINTS, InvalidMaxAccountingChangeThreshold());
PositionAccountingStorageLib.PositionAccountingStorage storage $ = PositionAccountingStorageLib.fetch();
uint64 oldThreshold = $.maxAccountingChangeThreshold;
emit MaxAccountingChangeThresholdSet(oldThreshold, maxAccountingChangeThreshold_);
$.maxAccountingChangeThreshold = maxAccountingChangeThreshold_;
}
/**
* @notice Sets the accounting change validation period
* @param accountingValidityPeriod_ The new accounting change validation period in seconds
*/
function setAccountingValidityPeriod(uint64 accountingValidityPeriod_) internal {
PositionAccountingStorageLib.PositionAccountingStorage storage $ = PositionAccountingStorageLib.fetch();
if (accountingValidityPeriod_ < $.cooldownPeriod + MIN_PERIOD_DIFFERENCE) {
revert InvalidAccountingValidityPeriod();
}
uint64 oldPeriod = $.accountingValidityPeriod;
emit SetAccountingValidityPeriod(oldPeriod, accountingValidityPeriod_);
$.accountingValidityPeriod = accountingValidityPeriod_;
}
/**
* @notice Sets the accounting update cooldown period
* @param cooldownPeriod_ The new update cooldown period in seconds
*/
function setCooldownPeriod(uint64 cooldownPeriod_) internal {
PositionAccountingStorageLib.PositionAccountingStorage storage $ = PositionAccountingStorageLib.fetch();
if (cooldownPeriod_ > $.accountingValidityPeriod - MIN_PERIOD_DIFFERENCE) revert InvalidCooldownPeriod();
uint64 oldPeriod = $.cooldownPeriod;
emit SetCooldownPeriod(oldPeriod, cooldownPeriod_);
$.cooldownPeriod = cooldownPeriod_;
}
/**
* @notice Returns the next accounting nonce
* @return The next accounting nonce
*/
function getNextAccountingNonce() internal view returns (uint256) {
return PositionAccountingStorageLib.fetch().accountingNonce + 1;
}
/**
* @notice Returns the last updated timestamp
* @return The last updated timestamp
*/
function getLastUpdatedTimestamp() internal view returns (uint64) {
PositionAccountingStorageLib.PositionAccountingStorage storage $ = PositionAccountingStorageLib.fetch();
return $.lastUpdatedTimestamp;
}
/**
* @notice Returns the max accounting change threshold
* @return The max accounting change threshold
*/
function getMaxAccountingChangeThreshold() internal view returns (uint64) {
PositionAccountingStorageLib.PositionAccountingStorage storage $ = PositionAccountingStorageLib.fetch();
return $.maxAccountingChangeThreshold;
}
/**
* @notice Returns the accounting validity period
* @return The accounting validity period
*/
function getAccountingValidityPeriod() internal view returns (uint64) {
PositionAccountingStorageLib.PositionAccountingStorage storage $ = PositionAccountingStorageLib.fetch();
return $.accountingValidityPeriod;
}
/**
* @notice Returns the update cooldown period
* @return The update cooldown period
*/
function getCooldownPeriod() internal view returns (uint64) {
PositionAccountingStorageLib.PositionAccountingStorage storage $ = PositionAccountingStorageLib.fetch();
return $.cooldownPeriod;
}
/**
* @notice Updates the timestamp and nonce in the position accounting storage
* @return newNonce The new nonce value after incrementing
*/
function updateTimestampAndNonce() internal returns (uint256 newNonce) {
PositionAccountingStorageLib.PositionAccountingStorage storage accounting$ =
PositionAccountingStorageLib.fetch();
accounting$.lastUpdatedTimestamp = uint64(block.timestamp);
accounting$.accountingNonce += 1;
return accounting$.accountingNonce;
}
/**
* @notice Checks if the accounting change is valid
*/
function _checkAccountingValidity() internal view {
PositionAccountingStorageLib.PositionAccountingStorage storage $ = PositionAccountingStorageLib.fetch();
if (block.timestamp - $.lastUpdatedTimestamp > $.accountingValidityPeriod) {
revert AccountingValidityPeriodExpired();
}
}
/**
* @notice Validates if an accounting change is acceptable
* @param diff the diff to adjust the accounting by
* @param accountingNonce_ The expected accounting nonce
* @param currentAccounting The current accounting amount
* @return isValid True if the change is valid (no pause needed), false if should pause
*/
function isValidAccountingChange(int256 diff, uint256 accountingNonce_, uint256 currentAccounting)
internal
returns (bool isValid)
{
PositionAccountingStorageLib.PositionAccountingStorage storage accounting$ =
PositionAccountingStorageLib.fetch();
if (accountingNonce_ != accounting$.accountingNonce + 1) {
revert InvalidAccountingNonce(accountingNonce_, accounting$.accountingNonce + 1);
}
return _validateAccountingChange(diff, accountingNonce_, currentAccounting);
}
/**
* @notice Checks if the change is acceptable
* @param diff the diff to adjust the accounting by
* @param accountingNonce_ The next accounting nonce of the accounting change
* @param currentAccounting The current accounting value
*/
function _validateAccountingChange(int256 diff, uint256 accountingNonce_, uint256 currentAccounting)
internal
returns (bool)
{
PositionAccountingStorageLib.PositionAccountingStorage storage accounting$ =
PositionAccountingStorageLib.fetch();
uint256 coolDownTimestamp = accounting$.lastUpdatedTimestamp + accounting$.cooldownPeriod;
bool isCooldownPeriodPassed = block.timestamp >= coolDownTimestamp;
if (!isCooldownPeriodPassed) {
emit CooldownPeriodNotPassed(accountingNonce_, block.timestamp, coolDownTimestamp);
}
uint256 divergence = diff < 0 ? uint256(-diff) : uint256(diff);
uint256 divergencePercentage = (divergence * BASIS_POINTS) / (currentAccounting + 1);
bool isAcceptableAccountingChange = divergencePercentage <= accounting$.maxAccountingChangeThreshold;
if (!isAcceptableAccountingChange) {
emit AccountingChangeTooLarge(
accountingNonce_, diff, currentAccounting, accounting$.maxAccountingChangeThreshold
);
}
return isCooldownPeriodPassed && isAcceptableAccountingChange;
}
}
"
},
"src/periphery/lib/PeripheryRolesLib.sol": {
"content": "// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;
/**
* @title PeripheryRolesLib
* @dev Library containing role definitions for periphery contracts
*/
library PeripheryRolesLib {
/// @dev Role for operators that can adjust total assets in strategies
bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE");
bytes32 public constant OPERATOR_ADMIN = keccak256("OPERATOR_ADMIN");
/// @dev Role for strategy administrators
bytes32 public constant STRATEGY_ADMIN = keccak256("STRATEGY_ADMIN");
bytes32 public constant STRATEGY_ADMIN_ADMIN = keccak256("STRATEGY_ADMIN_ADMIN");
}
"
},
"src/interface/IStrategyTemplate.sol": {
"content": "// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;
/**
* @title IStrategyTemplate
* @dev Interface that all strategy implementations must follow to be compatible with the vault system.
* @dev Each strategy is bound to a single vault and manages that vault's funds in different protocols or investment opportunities.
* @dev The Vault uses this interface to deploy, withdraw, and rebalance funds across multiple strategies.
*
* @notice This interface defines the core functionality required for strategy contracts:
* - Asset management (allocation and deallocation of funds)
* - Withdrawal capabilities for user redemptions
* - Limit reporting for rebalancing operations
* - Compatibility with the vault's underlying asset token
*
* @notice All strategies must implement proper access controls and ensure only authorized callers
* (typically the vault) can execute fund management operations.
*
* @notice For strategies that accrue rewards from underlying protocols:
* The vault has an arbitrary call execution function that can call any target with arbitrary data.
* This is primarily used to claim rewards from external reward systems. Strategies that earn rewards
* should provide dedicated functions that can be called by the vault through this mechanism to claim
* rewards and forward them to the rewards distributor system.
*/
/**
* @dev Enum representing different types of strategies
*/
enum StrategyType {
ATOMIC, // 0: Strategy that executes operations atomically, provides on-chain accurate accounting of yield
ASYNC, // 1: Strategy that requires asynchronous operations (multiple transactions), can provide stale (within defined latency) accounting of yield
CROSSCHAIN // 2: Strategy that operates across different blockchain networks, can provide stale (within defined latency) accounting of yield
}
interface IStrategyTemplate {
/**
* @dev Allocates funds from the vault to the underlying protocol.
* @dev This function will be called when the vault wants to deploy assets into the yield-generating protocol.
*
* @param data Arbitrary calldata that can be used to pass strategy-specific parameters for the allocation.
* This allows for flexible configuration of the allocation process (e.g., slippage tolerance,
* specific protocol parameters, routing information, etc.).
*
* - MUST emit the AllocateFunds event.
* - MUST revert if all of assets cannot be deposited (due to allocation limit being reached, slippage, the protocol
* not being able to accept more funds, etc).
*
* NOTE: most implementations will require pre-approval of the Vault with the Vault's underlying asset token.
*/
function allocateFunds(bytes calldata data) external returns (uint256);
/**
* @dev Deallocates funds from the underlying protocol back to the vault.
* @dev This function will be called when the vault wants to withdraw assets from the yield-generating protocol.
*
* @param data Arbitrary calldata that can be used to pass strategy-specific parameters for the deallocation.
* This allows for flexible configuration of the withdrawal process (e.g., slippage tolerance,
* specific protocol parameters, withdrawal routing, etc.).
*
* - MUST emit the DeallocateFunds event.
* - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the protocol
* not having enough liquidity, etc).
*/
function deallocateFunds(bytes calldata data) external returns (uint256);
/**
* @dev Sends assets of underlying tokens to sender.
* @dev This function will be called when the vault unwinds its position while depositor withdraws.
*
* - MUST emit the Withdraw event.
* - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner
* not having enough assets, etc).
*/
function onWithdraw(uint256 assets) external returns (uint256);
/**
* @dev Rescue function to withdraw tokens that may have been accidentally sent to the strategy.
* @dev This function allows authorized users to rescue tokens that are not part of the strategy's normal operations.
*
* @param token The address of the token to rescue.
* @param amount The amount of tokens to rescue. Use 0 to rescue all available tokens.
*
* - MUST only allow rescue of tokens that are not the strategy's primary asset (asset()).
* - MUST emit appropriate events for the rescue operation.
* - MUST revert if the caller is not authorized to perform token rescue.
* - MUST revert if attempting to rescue the strategy's primary asset token.
*/
function rescueToken(address token, uint256 amount) external;
/**
* @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);
/**
* @dev Returns the address of the vault that this strategy is bound to.
*
* - MUST return the vault address that was set during strategy initialization.
* - MUST NOT revert.
*/
function getVault() external view returns (address);
/**
* @dev Returns the type of strategy implementation.
* @dev This function indicates the operational characteristics of the strategy.
*
* @return The strategy type as defined in the StrategyType enum.
*
* - MUST return one of the defined StrategyType values.
* - MUST NOT revert.
* - ATOMIC: Strategy executes operations atomically in the same transaction, yield MUST be always atomicly updated in strategy allocated amount.
* - ASYNC: Strategy requires asynchronous operations across multiple transactions, yield Can be updated asynchronously within documented latency.
* - CROSSCHAIN: Strategy operates across different blockchain networks, yield Can be updated asynchronously within documented latency.
*/
function strategyType() external view returns (StrategyType);
/**
* @dev Returns the total value of assets that the bound vault has allocated in the strategy.
* @dev This function is mainly used during yield accrual operations to account for strategy yield or losses.
*
* @return The total value of allocated assets denominated in the asset() token.
*
* - MUST return the total value of assets that the bound vault has allocated to this strategy.
* - MUST account for any losses or depreciation in the underlying protocol.
* - MUST NOT revert.
* - MUST return 0 if the vault has no funds allocated to this strategy.
*/
function totalAllocatedValue() external view returns (uint256);
/**
* @dev Returns the maximum amount of assets that can be allocated to the underlying protocol.
* @dev This function is primarily used by the Allocator to determine allocation limits when rebalancing funds.
*
* - MUST return the maximum amount of underlying assets that can be allocated in a single call to allocateFunds.
* - MUST NOT revert.
* - MAY return 0 if the protocol cannot accept any more funds.
* - MAY return type(uint256).max if there is no practical limit.
*/
function maxAllocation() external view returns (uint256);
/**
* @dev Returns the maximum amount of assets that can be withdrawn from the strategy by the vault.
* @dev This function is primarily used by the vault to determine withdrawal limits when covering user redemptions.
*
* - MUST return the maximum amount of underlying assets that can be withdrawn in a single call to onWithdraw.
* - MUST NOT revert.
* - MAY return 0 if no funds are available for withdrawal.
* - SHOULD reflect current liquidity constraints and strategy-specific withdrawal limits.
*/
function maxWithdraw() external view returns (uint256);
}
"
},
"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/interfaces/IERC4626.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (interfaces/IERC4626.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../token/ERC20/IERC20.sol";
import {IERC20Metadata} from "../token/ERC20/extensions/IERC20Metadata.sol";
/**
* @dev Interface of the ERC-4626 "Tokenized Vault Standard", as defined in
* https://eips.ethereum.org/EIPS/eip-4626[ERC-4626].
*/
interface IERC4626 is IERC20, IERC20Metadata {
event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares);
event Withdraw(
address indexed sender,
address indexed receiver,
address indexed owner,
uint256 assets,
uint256 shares
);
/**
* @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 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 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.
* - 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 convertToAssets(uint256 shares) external view returns (uint256 assets);
/**
* @dev Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver,
* through a deposit call.
*
* - MUST return a limited value if receiver is subject to some deposit limit.
* - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited.
* - MUST NOT revert.
*/
function maxDeposit(address receiver) external view returns (uint256 maxAssets);
/**
* @dev Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given
* current on-chain conditions.
*
* - MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit
* call in the same transaction. I.e. deposit should return the same or more shares as previewDeposit if called
* in the same transaction.
* - MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the
* deposit would be accepted, regardless if the user has enough tokens approved, etc.
* - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
* - MUST NOT revert.
*
* NOTE: any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in
* share price or some other type of condition, meaning the depositor will lose assets by depositing.
*/
function previewDeposit(uint256 assets) external view returns (uint256 shares);
/**
* @dev Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens.
*
* - MUST emit the Deposit event.
* - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
* deposit execution, and are accounted for during deposit.
* - MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not
* approving enough underlying tokens to the Vault contract, etc).
*
* NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
*/
function deposit(uint256 assets, address receiver) external returns (uint256 shares);
/**
* @dev Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call.
* - MUST return a limited value if receiver is subject to some mint limit.
* - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted.
* - MUST NOT revert.
*/
function maxMint(address receiver) external view returns (uint256 maxShares);
/**
* @dev Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given
* current on-chain conditions.
*
* - MUST return as close to and no fewer than the exact amount of assets that would be deposited in a mint call
* in the same transaction. I.e. mint should return the same or fewer assets as previewMint if called in the
* same transaction.
* - MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint
* would be accepted, regardless if the user has enough tokens approved, etc.
* - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
* - MUST NOT revert.
*
* NOTE: any unfavorable discrepancy between convertToAssets and previewMint SHOULD be considered slippage in
* share price or some other type of condition, meaning the depositor will lose assets by minting.
*/
function previewMint(uint256 shares) external view returns (uint256 assets);
/**
* @dev Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens.
*
* - MUST emit the Deposit event.
* - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint
* execution, and are accounted for during mint.
* - MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not
* approving enough underlying tokens to the Vault contract, etc).
*
* NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
*/
function mint(uint256 shares, address receiver) external returns (uint256 assets);
/**
* @dev Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the
* Vault, through a withdraw call.
*
* - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
* - MUST NOT revert.
*/
function maxWithdraw(address owner) external view returns (uint256 maxAssets);
/**
* @dev Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block,
* given current on-chain conditions.
*
* - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw
* call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if
* called
* in the same transaction.
* - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though
* the withdrawal would be accepted, regardless if the user has enough shares, etc.
* - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
* - MUST NOT revert.
*
* NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in
* share price or some other type of condition, meaning the depositor will lose assets by depositing.
*/
function previewWithdraw(uint256 assets) external view returns (uint256 shares);
/**
* @dev Burns shares from owner and sends exactly assets of underlying tokens to receiver.
*
* - MUST emit the Withdraw event.
* - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
* withdraw execution, and are accounted for during withdraw.
* - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner
* not having enough shares, etc).
*
* Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
* Those methods should be performed separately.
*/
function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares);
/**
* @dev Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault,
* through a redeem call.
*
* - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
* - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock.
* - MUST NOT revert.
*/
function maxRedeem(address owner) external view returns (uint256 maxShares);
/**
* @dev Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block,
* given current on-chain conditions.
*
* - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call
* in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the
* same transaction.
* - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the
* redemption would be accepted, regardless if the user has enough shares, etc.
* - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
* - MUST NOT revert.
*
* NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in
* share price or some other type of condition, meaning the depositor will lose assets by redeeming.
*/
function previewRedeem(uint256 shares) external view returns (uint256 assets);
/**
* @dev Burns exactly shares from owner and sends assets of underlying tokens to receiver.
*
* - MUST emit the Withdraw event.
* - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
* redeem execution, and are accounted for during redeem.
* - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner
* not having enough shares, etc).
*
* NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
* Those methods should be performed separately.
*/
function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets);
}
"
},
"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 "../../../i
Submitted on: 2025-10-18 09:08:03
Comments
Log in to comment.
No comments yet.