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/vault/MorphoDestinationVault.sol": {
"content": "// SPDX-License-Identifier: UNLICENSED
// Copyright (c) 2025 Tokemak Foundation. All rights reserved.
pragma solidity ^0.8.24;
import { ISystemRegistry } from "src/interfaces/ISystemRegistry.sol";
import { IERC20 } from "openzeppelin-contracts/token/ERC20/IERC20.sol";
import { IDestinationVault } from "src/interfaces/vault/IDestinationVault.sol";
import { IUniversalRewardsDistributor } from "src/interfaces/external/morpho/IUniversalRewardsDistributor.sol";
import { Roles } from "src/libs/Roles.sol";
import { Errors } from "src/utils/Errors.sol";
import { ERC4626DestinationVault } from "src/vault/ERC4626DestinationVault.sol";
import { SafeERC20 } from "openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";
import { EnumerableSet } from "openzeppelin-contracts/utils/structs/EnumerableSet.sol";
import { IDistributor } from "src/interfaces/external/merkl/IDistributor.sol";
/// @title Destination Vault to interact with Morpho 4626 vaults
contract MorphoDestinationVault is ERC4626DestinationVault {
using SafeERC20 for IERC20;
using EnumerableSet for EnumerableSet.AddressSet;
string internal constant EXCHANGE_NAME = "morpho";
/// @dev Exposed via `getClaimableAssets()`
EnumerableSet.AddressSet internal registeredClaimableAssets;
// ------------------- //
// Events
// ------------------- //
event MerkleRewardsClaimed(address[] values);
event ClaimableAssetAddOrRemove(address[] assets, bool add);
// ------------------- //
// Errors
// ------------------- //
error InvalidArrayLengths(string info);
error ClaimableAssetNotRegistered(address asset);
error InvalidClaimAmount();
// ------------------- //
// Constructor
// ------------------- //
constructor(
ISystemRegistry sysRegistry
) ERC4626DestinationVault(sysRegistry) { }
// ------------------- //
// External functions
// ------------------- //
/// @inheritdoc IDestinationVault
function exchangeName() external pure override returns (string memory) {
return EXCHANGE_NAME;
}
/// @inheritdoc IDestinationVault
function poolType() external pure override returns (string memory) {
return "metaMorpho";
}
/// @inheritdoc IDestinationVault
function destType() external pure override returns (string memory) {
return "hold";
}
/// @notice Add assets that we can claim via merkle claiming
/// @param assets Addresses of assets to add
function addClaimableAssets(
address[] memory assets
) external hasRole(Roles.DESTINATION_VAULT_REWARD_MANAGER) {
uint256 length = assets.length;
for (uint256 i = 0; i < length; ++i) {
address currentAssetToAdd = assets[i];
Errors.verifyNotZero(currentAssetToAdd, "currentAssetToAdd");
if (!registeredClaimableAssets.add(currentAssetToAdd)) revert Errors.ItemExists();
}
emit ClaimableAssetAddOrRemove(assets, true);
}
/// @notice Remove assets that we can claim via merkle claiming
/// @param assets Addresses of assets to remove
function removeClaimableAssets(
address[] memory assets
) external hasRole(Roles.DESTINATION_VAULT_REWARD_MANAGER) {
uint256 length = assets.length;
for (uint256 i = 0; i < length; ++i) {
address currentAssetToRemove = assets[i];
Errors.verifyNotZero(currentAssetToRemove, "currentAssetToRemove");
if (!registeredClaimableAssets.remove(currentAssetToRemove)) revert Errors.ItemNotFound();
}
emit ClaimableAssetAddOrRemove(assets, false);
}
/// @notice Returns all registered claimable assets
function getClaimableAssets() external view returns (address[] memory claimable) {
claimable = registeredClaimableAssets.values();
}
/// @notice Allow a trusted operator to claim on behalf of this account
/// @dev Does not emit state change events
/// @param distributor Token distributor we'll call the fn on
/// @param trustedOperator Account allowed to claim on behalf of the Destination
function toggleMerklOperator(
address distributor,
address trustedOperator
) external hasRole(Roles.DESTINATION_MERKLE_CLAIM_MANAGER) {
IDistributor(distributor).toggleOperator(address(this), trustedOperator);
}
/// @notice Used to claim rewards distributed via merkle root for Morpho
/// @param rewardTokens tokens to be claimed. Must be registered on this contract first
/// @param rewardDistributors contracts distributing the rewards
/// @param cumulativeClaimable cumulative amount claimed
/// @param proofs merkle proof by claim
function collectMerkleRewards(
address[] memory rewardTokens,
address[] memory rewardDistributors,
uint256[] memory cumulativeClaimable,
bytes32[][] memory proofs
) external hasRole(Roles.DESTINATION_MERKLE_CLAIM_MANAGER) {
uint256 length = rewardTokens.length;
if (length != rewardDistributors.length || length != cumulativeClaimable.length || length != proofs.length) {
revert InvalidArrayLengths("rewardTokens+rewardDistributors+cumulativeClaimable+proofs");
}
emit MerkleRewardsClaimed(rewardTokens);
for (uint256 i = 0; i < length; ++i) {
address rewardToken = rewardTokens[i];
address rewardDistributor = rewardDistributors[i];
uint256 cumulative = cumulativeClaimable[i];
bytes32[] memory proof = proofs[i];
// Below check will catch a zero address for reward token. We check for zero address on registration
if (!registeredClaimableAssets.contains(rewardToken)) revert ClaimableAssetNotRegistered(rewardToken);
Errors.verifyNotZero(rewardDistributor, "rewardDistributor");
Errors.verifyNotZero(cumulative, "cumulative");
Errors.verifyNotZero(proof.length, "proof.length");
uint256 rewardTokenBalanceBefore = IERC20(rewardToken).balanceOf(address(this));
uint256 amountClaimed =
IUniversalRewardsDistributor(rewardDistributor).claim(address(this), rewardToken, cumulative, proof);
uint256 rewardTokenBalanceAfter = IERC20(rewardToken).balanceOf(address(this));
// Checking that a reward is claimed, and that amounts make sense
if (amountClaimed == 0 || rewardTokenBalanceAfter != rewardTokenBalanceBefore + amountClaimed) {
revert InvalidClaimAmount();
}
}
}
// ------------------- //
// Internal functions
// ------------------- //
/// @dev Has potential to return zero amounts
function _collectRewards() internal override returns (uint256[] memory amounts, address[] memory tokens) {
address[] memory allClaimableTokens = registeredClaimableAssets.values();
uint256 length = allClaimableTokens.length;
amounts = new uint256[](length);
tokens = new address[](length);
for (uint256 i = 0; i < length; ++i) {
IERC20 token = IERC20(allClaimableTokens[i]);
uint256 amount = token.balanceOf(address(this));
if (!isTrackedToken(address(token))) {
tokens[i] = address(token);
if (amount > 0) {
token.safeTransfer(msg.sender, amount);
amounts[i] = amount;
}
}
}
}
}
"
},
"src/interfaces/ISystemRegistry.sol": {
"content": "// SPDX-License-Identifier: UNLICENSED
// Copyright (c) 2023 Tokemak Foundation. All rights reserved.
pragma solidity ^0.8.24;
import { IWETH9 } from "src/interfaces/utils/IWETH9.sol";
import { IAccToke } from "src/interfaces/staking/IAccToke.sol";
import { IAutopoolRegistry } from "src/interfaces/vault/IAutopoolRegistry.sol";
import { IAccessController } from "src/interfaces/security/IAccessController.sol";
import { ISwapRouter } from "src/interfaces/swapper/ISwapRouter.sol";
import { ICurveResolver } from "src/interfaces/utils/ICurveResolver.sol";
import { IAutopilotRouter } from "src/interfaces/vault/IAutopilotRouter.sol";
import { IAutopoolFactory } from "src/interfaces/vault/IAutopoolFactory.sol";
import { ISystemSecurity } from "src/interfaces/security/ISystemSecurity.sol";
import { IDestinationRegistry } from "src/interfaces/destinations/IDestinationRegistry.sol";
import { IRootPriceOracle } from "src/interfaces/oracles/IRootPriceOracle.sol";
import { IDestinationVaultRegistry } from "src/interfaces/vault/IDestinationVaultRegistry.sol";
import { IAccessController } from "src/interfaces/security/IAccessController.sol";
import { IStatsCalculatorRegistry } from "src/interfaces/stats/IStatsCalculatorRegistry.sol";
import { IAsyncSwapperRegistry } from "src/interfaces/liquidation/IAsyncSwapperRegistry.sol";
import { IERC20Metadata } from "openzeppelin-contracts/token/ERC20/extensions/IERC20Metadata.sol";
import { IIncentivesPricingStats } from "src/interfaces/stats/IIncentivesPricingStats.sol";
import { IMessageProxy } from "src/interfaces/messageProxy/IMessageProxy.sol";
/// @notice Root most registry contract for the system
interface ISystemRegistry {
/// @notice Get the TOKE contract for the system
/// @return toke instance of TOKE used in the system
function toke() external view returns (IERC20Metadata);
/// @notice Get the referenced WETH contract for the system
/// @return weth contract pointer
function weth() external view returns (IWETH9);
/// @notice Get the AccToke staking contract
/// @return accToke instance of the accToke contract for the system
function accToke() external view returns (IAccToke);
/// @notice Get the AutopoolRegistry for this system
/// @return registry instance of the registry for this system
function autoPoolRegistry() external view returns (IAutopoolRegistry registry);
/// @notice Get the destination Vault registry for this system
/// @return registry instance of the registry for this system
function destinationVaultRegistry() external view returns (IDestinationVaultRegistry registry);
/// @notice Get the access Controller for this system
/// @return controller instance of the access controller for this system
function accessController() external view returns (IAccessController controller);
/// @notice Get the destination template registry for this system
/// @return registry instance of the registry for this system
function destinationTemplateRegistry() external view returns (IDestinationRegistry registry);
/// @notice Auto Pilot Router
/// @return router instance of the system
function autoPoolRouter() external view returns (IAutopilotRouter router);
/// @notice Vault factory lookup by type
/// @return vaultFactory instance of the vault factory for this vault type
function getAutopoolFactoryByType(
bytes32 vaultType
) external view returns (IAutopoolFactory vaultFactory);
/// @notice Get the stats calculator registry for this system
/// @return registry instance of the registry for this system
function statsCalculatorRegistry() external view returns (IStatsCalculatorRegistry registry);
/// @notice Get the root price oracle for this system
/// @return oracle instance of the root price oracle for this system
function rootPriceOracle() external view returns (IRootPriceOracle oracle);
/// @notice Get the async swapper registry for this system
/// @return registry instance of the registry for this system
function asyncSwapperRegistry() external view returns (IAsyncSwapperRegistry registry);
/// @notice Get the swap router for this system
/// @return router instance of the swap router for this system
function swapRouter() external view returns (ISwapRouter router);
/// @notice Get the curve resolver for this system
/// @return resolver instance of the curve resolver for this system
function curveResolver() external view returns (ICurveResolver resolver);
/// @notice Verify if given address is registered as Reward Token
/// @param rewardToken token address to verify
/// @return bool that indicates true if token is registered and false if not
function isRewardToken(
address rewardToken
) external view returns (bool);
/// @notice Get the system security instance for this system
/// @return security instance of system security for this system
function systemSecurity() external view returns (ISystemSecurity security);
/// @notice Get the Incentive Pricing Stats
/// @return incentivePricing the incentive pricing contract
function incentivePricing() external view returns (IIncentivesPricingStats);
/// @notice Get the Message Proxy
/// @return Message proxy contract
function messageProxy() external view returns (IMessageProxy);
/// @notice Get the receiving router contract.
/// @return Receiving router contract
function receivingRouter() external view returns (address);
/// @notice Check if an additional contract of type is valid in the system
/// @return True if the contract is a valid for the given type
function isValidContract(bytes32 contractType, address contractAddress) external view returns (bool);
/// @notice Returns the additional contract of the given type
/// @dev Revert if not set
function getUniqueContract(
bytes32 contractType
) external view returns (address);
/// @notice Returns all unique contracts configured
function listUniqueContracts() external view returns (bytes32[] memory contractTypes, address[] memory addresses);
/// @notice Returns all additional contract types configured
function listAdditionalContractTypes() external view returns (bytes32[] memory);
/// @notice Returns configured additional contracts by type
/// @param contractType Type of contract to list
function listAdditionalContracts(
bytes32 contractType
) external view returns (address[] memory);
}
"
},
"lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
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 amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` 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 amount) 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 `amount` 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 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` 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 amount
) external returns (bool);
}
"
},
"src/interfaces/vault/IDestinationVault.sol": {
"content": "// SPDX-License-Identifier: UNLICENSED
// Copyright (c) 2023 Tokemak Foundation. All rights reserved.
pragma solidity ^0.8.24;
import { IERC20Metadata as IERC20 } from "openzeppelin-contracts/token/ERC20/extensions/IERC20Metadata.sol";
import { IBaseAssetVault } from "src/interfaces/vault/IBaseAssetVault.sol";
import { IMainRewarder } from "src/interfaces/rewarders/IMainRewarder.sol";
import { IDexLSTStats } from "src/interfaces/stats/IDexLSTStats.sol";
import { ISystemComponent } from "src/interfaces/ISystemComponent.sol";
interface IDestinationVault is ISystemComponent, IBaseAssetVault, IERC20 {
enum VaultShutdownStatus {
Active,
Deprecated,
Exploit
}
error LogicDefect();
error BaseAmountReceived(uint256 amount);
/* ******************************** */
/* View */
/* ******************************** */
/// @notice A full unit of this vault
// solhint-disable-next-line func-name-mixedcase
function ONE() external view returns (uint256);
/// @notice The asset that is deposited into the vault
function underlying() external view returns (address);
/// @notice The total supply of the underlying asset
function underlyingTotalSupply() external view returns (uint256);
/// @notice The asset that rewards and withdrawals to the Autopool are denominated in
/// @inheritdoc IBaseAssetVault
function baseAsset() external view override returns (address);
/// @notice Debt balance of underlying asset that is in contract. This
/// value includes only assets that are known as debt by the rest of the
/// system (i.e. transferred in on rebalance), and does not include
/// extraneous amounts of underlyer that may have ended up in this contract.
function internalDebtBalance() external view returns (uint256);
/// @notice Debt balance of underlying asset staked externally. This value only
/// includes assets known as debt to the rest of the system, and does not include
/// any assets staked on behalf of the DV in external contracts.
function externalDebtBalance() external view returns (uint256);
/// @notice Returns true value of _underlyer in DV. Debt + tokens that may have
/// been transferred into the contract outside of rebalance.
function internalQueriedBalance() external view returns (uint256);
/// @notice Returns true value of staked _underlyer in external contract. This
/// will include any _underlyer that has been staked on behalf of the DV.
function externalQueriedBalance() external view returns (uint256);
/// @notice Balance of underlying debt, sum of `externalDebtBalance()` and `internalDebtBalance()`.
function balanceOfUnderlyingDebt() external view returns (uint256);
/// @notice Total balance of underlying, sum of queried balances minus debt balances.
function excessUnderlyingBalance() external view returns (uint256);
/// @notice Rewarder for this vault
function rewarder() external view returns (address);
/// @notice Exchange this destination vault points to
function exchangeName() external view returns (string memory);
/// @notice The type of pool associated with this vault
function poolType() external view returns (string memory);
/// @notice The type of pool plus any staking information
function destType() external view returns (string memory);
/// @notice Sets a token to be tracked or not
/// @param token Token to start or stop tracking
/// @param tracked True to track. False to stop.
function setTrackedToken(address token, bool tracked) external;
/// @notice Returns tokens that are protected from transfer
function trackedTokens() external view returns (address[] memory);
/// @notice Returns whether a token is tracked or not
function isTrackedToken(
address
) external view returns (bool);
/// @notice If the pool only deals in ETH when adding or removing liquidity
function poolDealInEth() external view returns (bool);
/// @notice Tokens that base asset can be swapped into
function underlyingTokens() external view returns (address[] memory);
/// @notice Gets the reserves of the underlying tokens
function underlyingReserves() external view returns (address[] memory tokens, uint256[] memory amounts);
/* ******************************** */
/* Events */
/* ******************************** */
event Donated(address sender, uint256 amount);
event Withdraw(
uint256 target, uint256 actual, uint256 debtLoss, uint256 claimLoss, uint256 fromIdle, uint256 fromDebt
);
event UpdateSignedMessage(bytes32 hash, bool flag);
/* ******************************** */
/* Errors */
/* ******************************** */
error ZeroAddress(string paramName);
error InvalidShutdownStatus(VaultShutdownStatus status);
/* ******************************** */
/* Functions */
/* ******************************** */
/// @notice Setup the contract. These will be cloned so no constructor
/// @param baseAsset_ Base asset of the system. WETH/USDC/etc
/// @param underlyer_ Underlying asset the vault will wrap
/// @param rewarder_ Reward tracker for this vault
/// @param incentiveCalculator_ Incentive calculator for this vault
/// @param additionalTrackedTokens_ Additional tokens that should be considered 'tracked'
/// @param params_ Any extra parameters needed to setup the contract
function initialize(
IERC20 baseAsset_,
IERC20 underlyer_,
IMainRewarder rewarder_,
address incentiveCalculator_,
address[] memory additionalTrackedTokens_,
bytes memory params_
) external;
function getRangePricesLP() external returns (uint256 spotPrice, uint256 safePrice, bool isSpotSafe);
/// @notice Calculates the current value of a portion of the debt based on shares
/// @dev Queries the current value of all tokens we have deployed, whether its a single place, multiple, staked, etc
/// @param shares The number of shares to value
/// @return value The current value of our debt in terms of the baseAsset
function debtValue(
uint256 shares
) external returns (uint256 value);
/// @notice Collects any earned rewards from staking, incentives, etc. Transfers to sender
/// @dev Should be limited to LIQUIDATOR_MANAGER. Rewards must be collected before claimed
/// @return amounts amount of rewards claimed for each token
/// @return tokens tokens claimed
function collectRewards() external returns (uint256[] memory amounts, address[] memory tokens);
/// @notice Pull any non-tracked token to the specified destination
/// @dev Should be limited to TOKEN_RECOVERY_MANAGER
function recover(address[] calldata tokens, uint256[] calldata amounts, address[] calldata destinations) external;
/// @notice Recovers any extra underlying both in DV and staked externally not tracked as debt.
/// @dev Should be limited to TOKEN_SAVER_ROLE.
/// @param destination The address to send excess underlyer to.
function recoverUnderlying(
address destination
) external;
/// @notice Deposit underlying to receive destination vault shares
/// @param amount amount of base lp asset to deposit
function depositUnderlying(
uint256 amount
) external returns (uint256 shares);
/// @notice Withdraw underlying by burning destination vault shares
/// @param shares amount of destination vault shares to burn
/// @param to destination of the underlying asset
/// @return amount underlyer amount 'to' received
function withdrawUnderlying(uint256 shares, address to) external returns (uint256 amount);
/// @notice Burn specified shares for underlyer swapped to base asset
/// @param shares amount of vault shares to burn
/// @param to destination of the base asset
/// @return amount base asset amount 'to' received
/// @return tokens the tokens burned to get the base asset
/// @return tokenAmounts the amount of the tokens burned to get the base asset
function withdrawBaseAsset(
uint256 shares,
address to
) external returns (uint256 amount, address[] memory tokens, uint256[] memory tokenAmounts);
/// @notice Mark this vault as shutdown so that autoPools can react
function shutdown(
VaultShutdownStatus reason
) external;
/// @notice True if the vault has been shutdown
function isShutdown() external view returns (bool);
/// @notice Returns the reason for shutdown (or `Active` if not shutdown)
function shutdownStatus() external view returns (VaultShutdownStatus);
/// @notice Stats contract for this vault
function getStats() external view returns (IDexLSTStats);
/// @notice get the marketplace rewards
/// @return rewardTokens list of reward token addresses
/// @return rewardRates list of reward rates
function getMarketplaceRewards() external returns (uint256[] memory rewardTokens, uint256[] memory rewardRates);
/// @notice Get the address of the underlying pool the vault points to
/// @return poolAddress address of the underlying pool
function getPool() external view returns (address poolAddress);
/// @notice Gets the spot price of the underlying LP token
/// @dev Price validated to be inside our tolerance against safe price. Will revert if outside.
/// @return price Value of 1 unit of the underlying LP token in terms of the base asset
function getValidatedSpotPrice() external returns (uint256 price);
/// @notice Gets the safe price of the underlying LP token
/// @dev Price validated to be inside our tolerance against spot price. Will revert if outside.
/// @return price Value of 1 unit of the underlying LP token in terms of the base asset
function getValidatedSafePrice() external returns (uint256 price);
/// @notice Get the lowest price we can get for the LP token
/// @dev This price can be attacked is not validate to be in any range
/// @return price Value of 1 unit of the underlying LP token in terms of the base asset
function getUnderlyerFloorPrice() external returns (uint256 price);
/// @notice Get the highest price we can get for the LP token
/// @dev This price can be attacked is not validate to be in any range
/// @return price Value of 1 unit of the underlying LP token in terms of the base asset
function getUnderlyerCeilingPrice() external returns (uint256 price);
/// @notice Allows to change the incentive calculator of destination vault
/// @dev Only works when vault is shutdown, also validates the calculator before updating
/// @param incentiveCalculator address of the new incentive calculator
function setIncentiveCalculator(
address incentiveCalculator
) external;
/// @notice Allows to change the extension contract
/// @dev Should be limited to DESTINATION_VAULT_MANAGER
/// @param extension contract address
function setExtension(
address extension
) external;
/// @notice Calls the execute function of the extension contract
/// @dev Should be limited to DESTINATION_VAULT_MANAGER
/// @dev Special care should be taken to ensure that balances hasn't been manipulated
/// @param data any data that the extension contract needs
function executeExtension(
bytes calldata data
) external;
/// @notice Returns the max recoup credit given during the withdraw of an undervalued destination
function recoupMaxCredit() external view returns (uint256);
}
"
},
"src/interfaces/external/morpho/IUniversalRewardsDistributor.sol": {
"content": "// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.24;
// slither-disable-start shadowing-local
/// @notice The pending root struct for a merkle tree distribution during the timelock.
struct PendingRoot {
/// @dev The submitted pending root.
bytes32 root;
/// @dev The optional ipfs hash containing metadata about the root (e.g. the merkle tree itself).
bytes32 ipfsHash;
/// @dev The timestamp at which the pending root can be accepted.
uint256 validAt;
}
/// @dev This interface is used for factorizing IUniversalRewardsDistributorStaticTyping and
/// IUniversalRewardsDistributor.
/// @dev Consider using the IUniversalRewardsDistributor interface instead of this one.
interface IUniversalRewardsDistributorBase {
function root() external view returns (bytes32);
function owner() external view returns (address);
function timelock() external view returns (uint256);
function ipfsHash() external view returns (bytes32);
function isUpdater(
address
) external view returns (bool);
function claimed(address, address) external view returns (uint256);
function acceptRoot() external;
function setRoot(bytes32 newRoot, bytes32 newIpfsHash) external;
function setTimelock(
uint256 newTimelock
) external;
function setRootUpdater(address updater, bool active) external;
function revokePendingRoot() external;
function setOwner(
address newOwner
) external;
function submitRoot(bytes32 newRoot, bytes32 ipfsHash) external;
function claim(
address account,
address reward,
uint256 claimable,
bytes32[] memory proof
) external returns (uint256 amount);
}
/// @dev This interface is inherited by the UniversalRewardsDistributor so that function signatures are checked by the
/// compiler.
/// @dev Consider using the IUniversalRewardsDistributor interface instead of this one.
interface IUniversalRewardsDistributorStaticTyping is IUniversalRewardsDistributorBase {
function pendingRoot() external view returns (bytes32 root, bytes32 ipfsHash, uint256 validAt);
}
/// @title IUniversalRewardsDistributor
/// @author Morpho Labs
/// @custom:contact security@morpho.org
/// @dev Use this interface for UniversalRewardsDistributor to have access to all the functions with the appropriate
/// function signatures.
interface IUniversalRewardsDistributor is IUniversalRewardsDistributorBase {
function pendingRoot() external view returns (PendingRoot memory);
}
// slither-disable-end shadowing-local
"
},
"src/libs/Roles.sol": {
"content": "// SPDX-License-Identifier: UNLICENSED
// Copyright (c) 2023 Tokemak Foundation. All rights reserved.
pragma solidity ^0.8.24;
library Roles {
// Naming Conventions:
// - Use MANAGER, CREATOR, UPDATER, ..., for roles primarily managing on-chain activities.
// - Use EXECUTOR for roles that trigger off-chain initiated actions.
// - Group roles by functional area for clarity.
// Destination Vault Management
bytes32 public constant DESTINATION_VAULT_FACTORY_MANAGER = keccak256("CREATE_DESTINATION_VAULT_ROLE");
bytes32 public constant DESTINATION_VAULT_REGISTRY_MANAGER = keccak256("DESTINATION_VAULT_REGISTRY_MANAGER");
bytes32 public constant DESTINATION_VAULT_MANAGER = keccak256("DESTINATION_VAULT_MANAGER");
bytes32 public constant DESTINATION_VAULT_REWARD_MANAGER = keccak256("DESTINATION_VAULT_REWARD_MANAGER");
bytes32 public constant FLUID_DESTINATION_VAULT_MANAGER = keccak256("FLUID_DESTINATION_VAULT_MANAGER");
bytes32 public constant DESTINATION_MERKLE_CLAIM_MANAGER = keccak256("DESTINATION_MERKLE_CLAIM_MANAGER");
bytes32 public constant EULER_REWARD_MANAGER = keccak256("EULER_REWARD_MANAGER");
bytes32 public constant EULER_REWARD_EXECUTOR = keccak256("EULER_REWARD_EXECUTOR");
// Auto Pool Factory and Registry Management
bytes32 public constant AUTO_POOL_REGISTRY_UPDATER = keccak256("REGISTRY_UPDATER");
bytes32 public constant AUTO_POOL_FACTORY_MANAGER = keccak256("AUTO_POOL_FACTORY_MANAGER");
bytes32 public constant AUTO_POOL_FACTORY_VAULT_CREATOR = keccak256("CREATE_POOL_ROLE");
// Auto Pool Management
bytes32 public constant AUTO_POOL_DESTINATION_UPDATER = keccak256("DESTINATION_VAULTS_UPDATER");
bytes32 public constant AUTO_POOL_FEE_UPDATER = keccak256("AUTO_POOL_FEE_SETTER_ROLE");
bytes32 public constant AUTO_POOL_PERIODIC_FEE_UPDATER = keccak256("AUTO_POOL_PERIODIC_FEE_SETTER_ROLE");
bytes32 public constant AUTO_POOL_REWARD_MANAGER = keccak256("AUTO_POOL_REWARD_MANAGER_ROLE");
bytes32 public constant AUTO_POOL_MANAGER = keccak256("AUTO_POOL_ADMIN");
bytes32 public constant REBALANCER = keccak256("REBALANCER_ROLE");
bytes32 public constant STATS_HOOK_POINTS_ADMIN = keccak256("STATS_HOOK_POINTS_ADMIN");
// Reward Management
bytes32 public constant LIQUIDATOR_MANAGER = keccak256("LIQUIDATOR_ROLE");
bytes32 public constant DV_REWARD_MANAGER = keccak256("DV_REWARD_MANAGER_ROLE");
bytes32 public constant REWARD_LIQUIDATION_MANAGER = keccak256("REWARD_LIQUIDATION_MANAGER");
bytes32 public constant EXTRA_REWARD_MANAGER = keccak256("EXTRA_REWARD_MANAGER_ROLE");
bytes32 public constant REWARD_LIQUIDATION_EXECUTOR = keccak256("REWARD_LIQUIDATION_EXECUTOR");
bytes32 public constant BANK_SWAP_MANAGER = keccak256("BANK_SWAP_MANAGER");
// Statistics and Reporting
bytes32 public constant STATS_CALC_REGISTRY_MANAGER = keccak256("STATS_CALC_REGISTRY_MANAGER");
bytes32 public constant STATS_CALC_FACTORY_MANAGER = keccak256("CREATE_STATS_CALC_ROLE");
bytes32 public constant STATS_CALC_FACTORY_TEMPLATE_MANAGER = keccak256("STATS_CALC_TEMPLATE_MGMT_ROLE");
bytes32 public constant STATS_SNAPSHOT_EXECUTOR = keccak256("STATS_SNAPSHOT_ROLE");
bytes32 public constant STATS_INCENTIVE_TOKEN_UPDATER = keccak256("STATS_INCENTIVE_TOKEN_UPDATER");
bytes32 public constant STATS_GENERAL_MANAGER = keccak256("STATS_GENERAL_MANAGER");
bytes32 public constant STATS_LST_ETH_TOKEN_EXECUTOR = keccak256("STATS_LST_ETH_TOKEN_EXECUTOR");
bytes32 public constant STATS_CACHE_SET_TRANSIENT_EXECUTOR = keccak256("STATS_CACHE_SET_TRANSIENT_EXECUTOR");
// Emergency Management
bytes32 public constant EMERGENCY_PAUSER = keccak256("EMERGENCY_PAUSER");
bytes32 public constant SEQUENCER_OVERRIDE_MANAGER = keccak256("SEQUENCER_OVERRIDE_MANAGER");
// Miscellaneous Roles
bytes32 public constant SOLVER = keccak256("SOLVER_ROLE");
bytes32 public constant AUTO_POOL_REPORTING_EXECUTOR = keccak256("AUTO_POOL_UPDATE_DEBT_REPORTING_ROLE");
bytes32 public constant STRATEGY_HOOK_CONFIGURATION = keccak256("STRATEGY_HOOK_CONFIGURATION");
// Swapper Roles
bytes32 public constant SWAP_ROUTER_MANAGER = keccak256("SWAP_ROUTER_MANAGER");
// Price Oracles Roles
bytes32 public constant ORACLE_MANAGER = keccak256("ORACLE_MANAGER_ROLE");
bytes32 public constant CUSTOM_ORACLE_EXECUTOR = keccak256("CUSTOM_ORACLE_EXECUTOR");
bytes32 public constant MAVERICK_FEE_ORACLE_EXECUTOR = keccak256("MAVERICK_FEE_ORACLE_MANAGER");
// AccToke Roles
bytes32 public constant ACC_TOKE_MANAGER = keccak256("ACC_TOKE_MANAGER");
// Admin Roles
bytes32 public constant TOKEN_RECOVERY_MANAGER = keccak256("TOKEN_RECOVERY_ROLE");
bytes32 public constant INFRASTRUCTURE_MANAGER = keccak256("INFRASTRUCTURE_MANAGER");
// Cross chain communications roles
bytes32 public constant MESSAGE_PROXY_MANAGER = keccak256("MESSAGE_PROXY_MANAGER");
bytes32 public constant MESSAGE_PROXY_EXECUTOR = keccak256("MESSAGE_PROXY_EXECUTOR");
bytes32 public constant RECEIVING_ROUTER_MANAGER = keccak256("RECEIVING_ROUTER_MANAGER");
bytes32 public constant RECEIVING_ROUTER_EXECUTOR = keccak256("RECEIVING_ROUTER_EXECUTOR");
// Backing Oracle
bytes32 public constant BACKING_ORACLE_MANAGER = keccak256("BACKING_ORACLE_MANAGER");
// Silo Vault Reward Manager
bytes32 public constant DESTINATION_VAULT_SILO_MANAGER = keccak256("DESTINATION_VAULT_SILO_MANAGER");
}
"
},
"src/utils/Errors.sol": {
"content": "// SPDX-License-Identifier: UNLICENSED
// Copyright (c) 2023 Tokemak Foundation. All rights reserved.
pragma solidity ^0.8.24;
import { Address } from "openzeppelin-contracts/utils/Address.sol";
import { ISystemComponent } from "src/interfaces/ISystemComponent.sol";
// solhint-disable max-line-length
library Errors {
using Address for address;
///////////////////////////////////////////////////////////////////
// Set errors
///////////////////////////////////////////////////////////////////
error AccessDenied();
error ZeroAddress(string paramName);
error ZeroAmount();
error InsufficientBalance(address token);
error AssetNotAllowed(address token);
error NotImplemented();
error InvalidAddress(address addr);
error InvalidParam(string paramName);
error InvalidParams();
error Exception(string desc);
error UnsafePrice(address token, uint256 spotPrice, uint256 safePrice);
error AlreadySet(string param);
error AlreadyRegistered(address param);
error SlippageExceeded(uint256 expected, uint256 actual);
error ArrayLengthMismatch(uint256 length1, uint256 length2, string details);
error ItemNotFound();
error ItemExists();
error MissingRole(bytes32 role, address user);
error RegistryItemMissing(string item);
error NotRegistered();
// Used to check storage slot is empty before setting.
error MustBeZero();
// Used to check storage slot set before deleting.
error MustBeSet();
error ApprovalFailed(address token);
error FlashLoanFailed(address token, uint256 amount);
error SystemMismatch(address source1, address source2);
error InvalidToken(address token);
error UnreachableError();
error InvalidSigner(address signer);
error InvalidChainId(uint256 chainId);
error SenderMismatch(address recipient, address sender);
error UnsupportedMessage(bytes32 messageType, bytes message);
error NotSupported();
error InvalidConfiguration();
error InvalidDataReturned();
function verifyNotZero(address addr, string memory paramName) internal pure {
if (addr == address(0)) {
revert ZeroAddress(paramName);
}
}
function verifyNotZero(bytes32 key, string memory paramName) internal pure {
if (key == bytes32(0)) {
revert InvalidParam(paramName);
}
}
function verifyNotEmpty(string memory val, string memory paramName) internal pure {
if (bytes(val).length == 0) {
revert InvalidParam(paramName);
}
}
function verifyNotZero(uint256 num, string memory paramName) internal pure {
if (num == 0) {
revert InvalidParam(paramName);
}
}
function verifySystemsMatch(address component1, address component2) internal view {
address registry1 =
abi.decode(component1.functionStaticCall(abi.encodeCall(ISystemComponent.getSystemRegistry, ())), (address));
address registry2 =
abi.decode(component2.functionStaticCall(abi.encodeCall(ISystemComponent.getSystemRegistry, ())), (address));
if (registry1 != registry2) {
revert SystemMismatch(component1, component2);
}
}
function verifyArrayLengths(uint256 length1, uint256 length2, string memory details) internal pure {
if (length1 != length2) {
revert ArrayLengthMismatch(length1, length2, details);
}
}
}
"
},
"src/vault/ERC4626DestinationVault.sol": {
"content": "// SPDX-License-Identifier: UNLICENSED
// Copyright (c) 2025 Tokemak Foundation. All rights reserved.
pragma solidity ^0.8.24;
import { IERC20 } from "openzeppelin-contracts/token/ERC20/IERC20.sol";
import { IERC4626 } from "src/interfaces/vault/IERC4626.sol";
import { ISystemRegistry } from "src/interfaces/ISystemRegistry.sol";
import { IStatsCalculator } from "src/interfaces/stats/IStatsCalculator.sol";
import { DestinationVault, IDestinationVault } from "src/vault/DestinationVault.sol";
/// @title Destination Vault to proxy interactions with ERC4626 vaults/tokens
contract ERC4626DestinationVault is DestinationVault {
error CalculatorAssetMismatch(address calculator, address id, address underlying);
constructor(
ISystemRegistry sysRegistry
) DestinationVault(sysRegistry) { }
/// @inheritdoc DestinationVault
function internalDebtBalance() public view override returns (uint256) {
return totalSupply();
}
/// @inheritdoc DestinationVault
function externalDebtBalance() public view virtual override returns (uint256) {
return 0;
}
/// @inheritdoc IDestinationVault
function externalQueriedBalance() public view virtual override returns (uint256) {
return 0;
}
/// @inheritdoc IDestinationVault
function exchangeName() external pure virtual override returns (string memory) {
return "none";
}
/// @inheritdoc IDestinationVault
function poolType() external pure virtual override returns (string memory) {
return "self";
}
/// @inheritdoc IDestinationVault
function destType() external pure virtual override returns (string memory) {
return "hold";
}
/// @inheritdoc IDestinationVault
function poolDealInEth() external pure override returns (bool) {
return false;
}
/// @inheritdoc IDestinationVault
function underlyingTotalSupply() external view override returns (uint256) {
return IERC20(_underlying).totalSupply();
}
/// @inheritdoc IDestinationVault
function underlyingTokens() external view virtual override returns (address[] memory result) {
result = new address[](1);
result[0] = IERC4626(_underlying).asset();
}
/// @inheritdoc IDestinationVault
function underlyingReserves()
external
view
virtual
override
returns (address[] memory tokens, uint256[] memory amounts)
{
tokens = new address[](1);
amounts = new uint256[](1);
tokens[0] = IERC4626(_underlying).asset();
amounts[0] = IERC4626(_underlying).totalAssets();
}
/// @inheritdoc DestinationVault
function _onDeposit(
uint256 amount
) internal virtual override {
// Do nothing
}
/// @inheritdoc DestinationVault
function _ensureLocalUnderlyingBalance(
uint256 amount
) internal virtual override {
// Do nothing, and return the empty amount
}
/// @inheritdoc DestinationVault
function _collectRewards() internal virtual override returns (uint256[] memory amounts, address[] memory tokens) {
// Do nothing and return empty amounts and tokens
}
/// @inheritdoc DestinationVault
function _burnUnderlyer(
uint256 underlyerAmount
) internal virtual override returns (address[] memory tokens, uint256[] memory amounts) {
tokens = new address[](1);
tokens[0] = IERC4626(_underlying).asset();
amounts = new uint256[](1);
amounts[0] = IERC4626(_underlying).redeem(underlyerAmount, address(this), address(this));
}
/// @inheritdoc DestinationVault
function getPool() public view virtual override returns (address) {
return _underlying;
}
/// @inheritdoc DestinationVault
function _validateCalculator(
address calculator
) internal view override {
address id = IStatsCalculator(calculator).getAddressId();
if (id != _underlying) {
revert CalculatorAssetMismatch(calculator, id, _underlying);
}
}
}
"
},
"lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../extensions/draft-IERC20Permit.sol";
import "../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using Address for address;
function safeTransfer(
IERC20 token,
address to,
uint256 value
) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(
IERC20 token,
address from,
address to,
uint256 value
) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
/**
* @dev Deprecated. This function has issues similar to the ones found in
* {IERC20-approve}, and its usage is discouraged.
*
* Whenever possible, use {safeIncreaseAllowance} and
* {safeDecreaseAllowance} instead.
*/
function safeApprove(
IERC20 token,
address spender,
uint256 value
) internal {
// safeApprove should only be called when setting an initial allowance,
// or when resetting it to zero. To increase and decrease it, use
// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
require(
(value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
function safeIncreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
uint256 newAllowance = token.allowance(address(this), spender) + value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function safeDecreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
uint256 newAllowance = oldAllowance - value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
}
function safePermit(
IERC20Permit token,
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
uint256 nonceBefore = token.nonces(owner);
token.permit(owner, spender, value, deadline, v, r, s);
uint256 nonceAfter = token.nonces(owner);
require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
if (returndata.length > 0) {
// Return data is optional
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}
"
},
"lib/openzeppelin-contracts/contracts/utils/structs/EnumerableSet.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.
pragma solidity ^0.8.0;
/**
* @dev Library for managing
* https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
* types.
*
* Sets have the following properties:
*
* - Elements are added, removed, and checked for existence in constant time
* (O(1)).
* - Elements are enumerated in O(n). No guarantees are made on the ordering.
*
* ```
* contract Example {
* // Add the library methods
* using EnumerableSet for EnumerableSet.AddressSet;
*
* // Declare a set state variable
* EnumerableSet.AddressSet private mySet;
* }
* ```
*
* As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
* and `uint256` (`UintSet`) are supported.
*
* [WARNING]
* ====
* Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
* unusable.
* See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
*
* In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
* array of EnumerableSet.
* ====
*/
library EnumerableSet {
// To implement this library for multiple types with as little code
// repetition as possible, we write it in terms of a generic Set type with
// bytes32 values.
// The Set implementation uses private functions, and user-facing
// implementations (such as AddressSet) are just wrappers around the
// underlying Set.
// This means that we can only create new EnumerableSets for types that fit
// in bytes32.
struct Set {
// Storage of set values
bytes32[] _values;
// Position of the value in the `values` array, plus 1 because index 0
// means a value is not in the set.
mapping(bytes32 => uint256) _indexes;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function _add(Set storage set, bytes32 value) private returns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
// The value is stored at length-1, but we add 1 to all indexes
// and use 0 as a sentinel value
set._indexes[value] = set._values.length;
return true;
} else {
return false;
}
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function _remove(Set storage set, bytes32 value) private returns (bool) {
// We read and store the value's index to prevent multiple reads from the same storage slot
uint256 valueIndex = set._indexes[value];
if (valueIndex != 0) {
// Equivalent to contains(set, value)
// To delete an element from the _values array in O(1), we swap the element to delete with the last one in
// the array, and then remove the last element (sometimes called as 'swap and pop').
// This modifies the order of the array, as noted in {at}.
uint256 toDeleteIndex = valueIndex - 1;
uint256 lastIndex = set._values.length - 1;
if (lastIndex != toDeleteIndex) {
bytes32 lastValue = set._values[lastIndex];
// Move the last value to the index where the value to delete is
set._values[toDeleteIndex] = lastValue;
// Update the index for the moved value
set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
}
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the index for the deleted slot
delete set._indexes[value];
return true;
} else {
return false;
}
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function _contains(Set storage set, bytes32 value) private view returns (bool) {
return set._indexes[value] != 0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function _at(Set storage set, uint256 index) private view returns (bytes32) {
return set._values[index];
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function _values(Set storage set) private view returns (bytes32[] memory) {
return set._values;
}
// Bytes32Set
struct Bytes32Set {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _add(set._inner, value);
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _remove(set._inner, value);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
return _contains(set._inner, value);
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(Bytes32Set storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
return _at(set._inner, index);
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
bytes32[] memory store = _values(set._inner);
bytes32[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// AddressSet
struct AddressSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(AddressSet storage set, address value) internal returns (bool) {
return _remove(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(AddressSet storage set, address value) internal view returns (bool) {
return _contains(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(AddressSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(AddressSet storage set, uint256 index) internal view returns (address) {
return address(uint160(uint256(_at(set._inner, index))));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(AddressSet storage set) internal view returns (address[] memory) {
bytes32[] memory store = _values(set._inner);
address[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// UintSet
struct UintSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(UintSet storage set, uint256 value) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(UintSet storage set, uint256 value) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(UintSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(UintSet storage set) internal view returns (uint256[] memory) {
bytes32[] memory store = _values(set._inner);
uint256[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
}
"
},
"src/interfaces/external/merkl/IDistributor.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;
interface IDistributor {
/// @notice Claims rewards for a given set of users
/// @dev Unless another address has been approved for claiming, only an address can claim for itself
/// @param users Addresses for which claiming is taking place
/// @param tokens ERC20 token claimed
/// @param amounts Amount of tokens that will be sent to the corresponding users
/// @param proofs Array of hashes bridging from a leaf `(hash of user | token | amount)` to the Merkle root
function claim(
address[] calldata users,
address[] calldata tokens,
uint256[] calldata amounts,
bytes32[][] calldata proofs
) external;
/// @notice Struct to track claims
struct Claim {
uint208 amount;
uint48 timestamp;
bytes32 merkleRoot;
}
/// @notice Returns claim information for a given user and token
/// @param user The address of the user
/// @para
Submitted on: 2025-10-01 20:45:52
Comments
Log in to comment.
No comments yet.