// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v4.8.0) (access/AccessControl.sol)pragmasolidity ^0.8.0;import"./IAccessControl.sol";
import"../utils/Context.sol";
import"../utils/Strings.sol";
import"../utils/introspection/ERC165.sol";
/**
* @dev Contract module that allows children to implement role-based access
* control mechanisms. This is a lightweight version that doesn't allow enumerating role
* members except through off-chain means by accessing the contract event logs. Some
* applications may benefit from on-chain enumerability, for those cases see
* {AccessControlEnumerable}.
*
* Roles are referred to by their `bytes32` identifier. These should be exposed
* in the external API and be unique. The best way to achieve this is by
* using `public constant` hash digests:
*
* ```
* bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
* ```
*
* Roles can be used to represent a set of permissions. To restrict access to a
* function call, use {hasRole}:
*
* ```
* function foo() public {
* require(hasRole(MY_ROLE, msg.sender));
* ...
* }
* ```
*
* Roles can be granted and revoked dynamically via the {grantRole} and
* {revokeRole} functions. Each role has an associated admin role, and only
* accounts that have a role's admin role can call {grantRole} and {revokeRole}.
*
* By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
* that only accounts with this role will be able to grant or revoke other
* roles. More complex role relationships can be created by using
* {_setRoleAdmin}.
*
* WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
* grant and revoke this role. Extra precautions should be taken to secure
* accounts that have been granted it.
*/abstractcontractAccessControlisContext, IAccessControl, ERC165{
structRoleData {
mapping(address=>bool) members;
bytes32 adminRole;
}
mapping(bytes32=> RoleData) private _roles;
bytes32publicconstant DEFAULT_ADMIN_ROLE =0x00;
/**
* @dev Modifier that checks that an account has a specific role. Reverts
* with a standardized message including the required role.
*
* The format of the revert reason is given by the following regular expression:
*
* /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
*
* _Available since v4.1._
*/modifieronlyRole(bytes32 role) {
_checkRole(role);
_;
}
/**
* @dev See {IERC165-supportsInterface}.
*/functionsupportsInterface(bytes4 interfaceId) publicviewvirtualoverridereturns (bool) {
return interfaceId ==type(IAccessControl).interfaceId||super.supportsInterface(interfaceId);
}
/**
* @dev Returns `true` if `account` has been granted `role`.
*/functionhasRole(bytes32 role, address account) publicviewvirtualoverridereturns (bool) {
return _roles[role].members[account];
}
/**
* @dev Revert with a standard message if `_msgSender()` is missing `role`.
* Overriding this function changes the behavior of the {onlyRole} modifier.
*
* Format of the revert message is described in {_checkRole}.
*
* _Available since v4.6._
*/function_checkRole(bytes32 role) internalviewvirtual{
_checkRole(role, _msgSender());
}
/**
* @dev Revert with a standard message if `account` is missing `role`.
*
* The format of the revert reason is given by the following regular expression:
*
* /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
*/function_checkRole(bytes32 role, address account) internalviewvirtual{
if (!hasRole(role, account)) {
revert(
string(
abi.encodePacked(
"AccessControl: account ",
Strings.toHexString(account),
" is missing role ",
Strings.toHexString(uint256(role), 32)
)
)
);
}
}
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {_setRoleAdmin}.
*/functiongetRoleAdmin(bytes32 role) publicviewvirtualoverridereturns (bytes32) {
return _roles[role].adminRole;
}
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*
* May emit a {RoleGranted} event.
*/functiongrantRole(bytes32 role, address account) publicvirtualoverrideonlyRole(getRoleAdmin(role)) {
_grantRole(role, account);
}
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*
* May emit a {RoleRevoked} event.
*/functionrevokeRole(bytes32 role, address account) publicvirtualoverrideonlyRole(getRoleAdmin(role)) {
_revokeRole(role, account);
}
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been revoked `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `account`.
*
* May emit a {RoleRevoked} event.
*/functionrenounceRole(bytes32 role, address account) publicvirtualoverride{
require(account == _msgSender(), "AccessControl: can only renounce roles for self");
_revokeRole(role, account);
}
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event. Note that unlike {grantRole}, this function doesn't perform any
* checks on the calling account.
*
* May emit a {RoleGranted} event.
*
* [WARNING]
* ====
* This function should only be called from the constructor when setting
* up the initial roles for the system.
*
* Using this function in any other way is effectively circumventing the admin
* system imposed by {AccessControl}.
* ====
*
* NOTE: This function is deprecated in favor of {_grantRole}.
*/function_setupRole(bytes32 role, address account) internalvirtual{
_grantRole(role, account);
}
/**
* @dev Sets `adminRole` as ``role``'s admin role.
*
* Emits a {RoleAdminChanged} event.
*/function_setRoleAdmin(bytes32 role, bytes32 adminRole) internalvirtual{
bytes32 previousAdminRole = getRoleAdmin(role);
_roles[role].adminRole = adminRole;
emit RoleAdminChanged(role, previousAdminRole, adminRole);
}
/**
* @dev Grants `role` to `account`.
*
* Internal function without access restriction.
*
* May emit a {RoleGranted} event.
*/function_grantRole(bytes32 role, address account) internalvirtual{
if (!hasRole(role, account)) {
_roles[role].members[account] =true;
emit RoleGranted(role, account, _msgSender());
}
}
/**
* @dev Revokes `role` from `account`.
*
* Internal function without access restriction.
*
* May emit a {RoleRevoked} event.
*/function_revokeRole(bytes32 role, address account) internalvirtual{
if (hasRole(role, account)) {
_roles[role].members[account] =false;
emit RoleRevoked(role, account, _msgSender());
}
}
}
Contract Source Code
File 2 of 31: Address.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)pragmasolidity ^0.8.1;/**
* @dev Collection of functions related to the address type
*/libraryAddress{
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/functionisContract(address account) internalviewreturns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0// for contracts in construction, since the code is only stored at the end// of the constructor execution.return account.code.length>0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/functionsendValue(addresspayable recipient, uint256 amount) internal{
require(address(this).balance>= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/functionfunctionCall(address target, bytesmemory data) internalreturns (bytesmemory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/functionfunctionCall(address target,
bytesmemory data,
stringmemory errorMessage
) internalreturns (bytesmemory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/functionfunctionCallWithValue(address target,
bytesmemory data,
uint256 value
) internalreturns (bytesmemory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/functionfunctionCallWithValue(address target,
bytesmemory data,
uint256 value,
stringmemory errorMessage
) internalreturns (bytesmemory) {
require(address(this).balance>= value, "Address: insufficient balance for call");
(bool success, bytesmemory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/functionfunctionStaticCall(address target, bytesmemory data) internalviewreturns (bytesmemory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/functionfunctionStaticCall(address target,
bytesmemory data,
stringmemory errorMessage
) internalviewreturns (bytesmemory) {
(bool success, bytesmemory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/functionfunctionDelegateCall(address target, bytesmemory data) internalreturns (bytesmemory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/functionfunctionDelegateCall(address target,
bytesmemory data,
stringmemory errorMessage
) internalreturns (bytesmemory) {
(bool success, bytesmemory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/functionverifyCallResultFromTarget(address target,
bool success,
bytesmemory returndata,
stringmemory errorMessage
) internalviewreturns (bytesmemory) {
if (success) {
if (returndata.length==0) {
// only check isContract if the call was successful and the return data is empty// otherwise we already know that it was a contractrequire(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or using the provided one.
*
* _Available since v4.3._
*/functionverifyCallResult(bool success,
bytesmemory returndata,
stringmemory errorMessage
) internalpurereturns (bytesmemory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function_revert(bytesmemory returndata, stringmemory errorMessage) privatepure{
// Look for revert reason and bubble it up if presentif (returndata.length>0) {
// The easiest way to bubble the revert reason is using memory via assembly/// @solidity memory-safe-assemblyassembly {
let returndata_size :=mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
// SPDX-License-Identifier: AGPL-3.0-onlypragmasolidity ^0.8.19;import {TokenOutNotAllowed} from"../errors/scErrors.sol";
import {ERC20} from"solmate/tokens/ERC20.sol";
import {Address} from"openzeppelin-contracts/utils/Address.sol";
import {EnumerableMap} from"openzeppelin-contracts/utils/structs/EnumerableMap.sol";
import {ProtocolNotSupported, ProtocolInUse, ZeroAddress} from"../errors/scErrors.sol";
import {ConstantsasC} from"../lib/Constants.sol";
import {IVault} from"../interfaces/balancer/IVault.sol";
import {IFlashLoanRecipient} from"../interfaces/balancer/IFlashLoanRecipient.sol";
import {IAdapter} from"./IAdapter.sol";
import {PriceConverter} from"./PriceConverter.sol";
import {Swapper} from"./Swapper.sol";
import {sc4626} from"../sc4626.sol";
/**
* @title BaseV2Vault
* @notice Base vault contract for v2 vaults to that use multiple lending markets thru adapters.
*/abstractcontractBaseV2Vaultissc4626, IFlashLoanRecipient{
usingAddressforaddress;
usingEnumerableMapforEnumerableMap.UintToAddressMap;
eventSwapperUpdated(addressindexed admin, address newSwapper);
eventProtocolAdapterAdded(addressindexed admin, uint256 adapterId, address adapter);
eventProtocolAdapterRemoved(addressindexed admin, uint256 adapterId);
eventRewardsClaimed(uint256 adapterId);
eventTokenSwapped(address token, uint256 amount, uint256 amountReceived);
eventTokenWhitelisted(address token, bool value);
// Balancer vault for flashloans
IVault publicconstant balancerVault = IVault(C.BALANCER_VAULT);
// price converter contract
PriceConverter publicimmutable priceConverter;
// swapper contract for facilitating token swaps
Swapper public swapper;
// mapping of IDs to lending protocol adapter contracts
EnumerableMap.UintToAddressMap internal protocolAdapters;
// mapping for the tokenOuts allowed during zeroExSwapmapping(ERC20 =>bool) internal zeroExSwapWhitelist;
constructor(address _admin,
address _keeper,
ERC20 _asset,
PriceConverter _priceConverter,
Swapper _swapper,
stringmemory _name,
stringmemory _symbol
) sc4626(_admin, _keeper, _asset, _name, _symbol) {
_zeroAddressCheck(address(_priceConverter));
_zeroAddressCheck(address(_swapper));
priceConverter = _priceConverter;
swapper = _swapper;
zeroExSwapWhitelist[_asset] =true;
}
/**
* @notice whitelist (or cancel whitelist) a token to be swapped out using zeroExSwap
* @param _token The token to whitelist
* @param _value whether to whitelist or cancel whitelist
*/functionwhiteListOutToken(ERC20 _token, bool _value) external{
_onlyAdmin();
if (address(_token) ==address(0)) revert ZeroAddress();
zeroExSwapWhitelist[_token] = _value;
emit TokenWhitelisted(address(_token), _value);
}
/**
* @notice Set the swapper contract used for executing token swaps.
* @param _newSwapper The new swapper contract.
*/functionsetSwapper(Swapper _newSwapper) external{
_onlyAdmin();
if (address(_newSwapper) ==address(0)) revert ZeroAddress();
swapper = _newSwapper;
emit SwapperUpdated(msg.sender, address(_newSwapper));
}
/**
* @notice Add a new protocol adapter to the vault.
* @param _adapter The adapter to add.
*/functionaddAdapter(IAdapter _adapter) external{
_onlyAdmin();
uint256 id = _adapter.id();
if (isSupported(id)) revert ProtocolInUse(id);
protocolAdapters.set(id, address(_adapter));
address(_adapter).functionDelegateCall(abi.encodeWithSelector(IAdapter.setApprovals.selector));
emit ProtocolAdapterAdded(msg.sender, id, address(_adapter));
}
/**
* @notice Remove a protocol adapter from the vault. Reverts if the adapter is in use unless _force is true.
* @param _adapterId The ID of the adapter to remove.
* @param _force Whether or not to force the removal of the adapter.
*/functionremoveAdapter(uint256 _adapterId, bool _force) external{
_onlyAdmin();
_isSupportedCheck(_adapterId);
// check if protocol is being usedif (!_force && IAdapter(protocolAdapters.get(_adapterId)).getCollateral(address(this)) >0) {
revert ProtocolInUse(_adapterId);
}
_adapterDelegateCall(_adapterId, abi.encodeWithSelector(IAdapter.revokeApprovals.selector));
protocolAdapters.remove(_adapterId);
emit ProtocolAdapterRemoved(msg.sender, _adapterId);
}
/**
* @notice Check if a lending market adapter is supported/used.
* @param _adapterId The ID of the lending market adapter.
*/functionisSupported(uint256 _adapterId) publicviewreturns (bool) {
return protocolAdapters.contains(_adapterId);
}
/**
* @notice returns whether a token is whitelisted to be swapped out using zeroExSwap or not
*/functionisTokenWhitelisted(ERC20 _token) externalviewreturns (bool) {
return zeroExSwapWhitelist[_token];
}
/**
* @notice Claim rewards from a lending market.
* @param _adapterId The ID of the lending market adapter.
* @param _callData The encoded data for the claimRewards function.
*/functionclaimRewards(uint256 _adapterId, bytescalldata _callData) external{
_onlyKeeperOrFlashLoan();
_isSupportedCheck(_adapterId);
_adapterDelegateCall(_adapterId, abi.encodeWithSelector(IAdapter.claimRewards.selector, _callData));
emit RewardsClaimed(_adapterId);
}
/**
* @notice Sell any token for the "asset" token on 0x exchange.
* @param _tokenIn The token to sell.
* @param _tokenOut The token to buy.
* @param _amount The amount of tokens to sell.
* @param _swapData The swap data for 0xrouter.
* @param _assetAmountOutMin The minimum amount of "asset" token to receive for the swap.
*/functionzeroExSwap(
ERC20 _tokenIn,
ERC20 _tokenOut,
uint256 _amount,
bytescalldata _swapData,
uint256 _assetAmountOutMin
) external{
_onlyKeeperOrFlashLoan();
if (!zeroExSwapWhitelist[_tokenOut]) revert TokenOutNotAllowed(address(_tokenOut));
bytesmemory result =address(swapper).functionDelegateCall(
abi.encodeWithSelector(
Swapper.zeroExSwap.selector, _tokenIn, _tokenOut, _amount, _assetAmountOutMin, _swapData
)
);
emit TokenSwapped(address(_tokenIn), _amount, abi.decode(result, (uint256)));
}
function_multiCall(bytes[] memory _callData) internal{
for (uint256 i =0; i < _callData.length; i++) {
if (_callData[i].length==0) continue;
address(this).functionDelegateCall(_callData[i]);
}
}
function_adapterDelegateCall(uint256 _adapterId, bytesmemory _data) internal{
protocolAdapters.get(_adapterId).functionDelegateCall(_data);
}
function_adapterDelegateCall(address _adapter, bytesmemory _data) internal{
_adapter.functionDelegateCall(_data);
}
function_isSupportedCheck(uint256 _adapterId) internalview{
if (!isSupported(_adapterId)) revert ProtocolNotSupported(_adapterId);
}
function_zeroAddressCheck(address _address) internalpure{
if (_address ==address(0)) revert ZeroAddress();
}
}
Contract Source Code
File 5 of 31: Constants.sol
// SPDX-License-Identifier: AGPL-3.0-onlypragmasolidity ^0.8.13;libraryConstants{
// address of the multisig. meant to have default admin roleaddresspublicconstant MULTISIG =0x035F210e5d14054E8AE5A6CFA76d643aA200D56E;
uint256publicconstant ONE =1e18;
// decimals difference between WETH and USDC (18 - 6)uint256publicconstant WETH_USDC_DECIMALS_DIFF =1e12;
// value for the variable interest rate mode on Aaveuint256publicconstant AAVE_VAR_INTEREST_RATE_MODE =2;
// enable efficeincy mode on Aave (used to allow greater LTV when asset and debt tokens are correlated in price)uint8publicconstant AAVE_EMODE_ID =1;
// vaule used to scale the token's collateral/borrow factors from the euler marketuint32constant EULER_CONFIG_FACTOR_SCALE =4_000_000_000;
/*//////////////////////////////////////////////////////////////
MAINNET ADDRESSES
//////////////////////////////////////////////////////////////*/// address of the USDC token contractaddresspublicconstant USDC =0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
// address of the WETH token contractaddresspublicconstant WETH =0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
// address of the wrapped stETH token contractaddresspublicconstant WSTETH =0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0;
// address of the Lido stETH token contractaddresspublicconstant STETH =0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84;
// address of the LUSD token contractaddresspublicconstant LUSD =0x5f98805A4E8be255a32880FDeC7F6728C6568bA0;
// address of the Curve pool for ETH-stETHaddresspublicconstant CURVE_ETH_STETH_POOL =0xDC24316b9AE028F1497c275EB9192a3Ea0f67022;
// address of the Uniswap v3 swap router contractaddresspublicconstant UNISWAP_V3_SWAP_ROUTER =0xE592427A0AEce92De3Edee1F18E0157C05861564;
// address of the Aave v3 pool contractaddresspublicconstant AAVE_V3_POOL =0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2;
// address of the Aave pool data provider contractaddresspublicconstant AAVE_V3_POOL_DATA_PROVIDER =0x7B4EB56E7CD4b454BA8ff71E4518426369a138a3;
// address of the Aave v3 "aEthUSDC" token (supply token)addresspublicconstant AAVE_V3_AUSDC_TOKEN =0x98C23E9d8f34FEFb1B7BD6a91B7FF122F4e16F5c;
// address of the Aave v3 "aEthwstETH" token (supply token)addresspublicconstant AAVE_V3_AWSTETH_TOKEN =0x0B925eD163218f6662a35e0f0371Ac234f9E9371;
// address of the Aave v3 "variableDebtEthWETH" token (variable debt token)addresspublicconstant AAVE_V3_VAR_DEBT_WETH_TOKEN =0xeA51d7853EEFb32b6ee06b1C12E6dcCA88Be0fFE;
// address of the Aave v3 "variableDebtEthWETH" token implementation contract (variable debt token)addresspublicconstant AAVE_V3_VAR_DEBT_IMPLEMENTATION_CONTRACT =0xaC725CB59D16C81061BDeA61041a8A5e73DA9EC6;
// EULER Contractsaddresspublicconstant EULER =0x27182842E098f60e3D576794A5bFFb0777E025d3;
addresspublicconstant EULER_MARKETS =0x3520d5a913427E6F0D6A83E07ccD4A4da316e4d3;
// Euler supply token for wstETH (ewstETH)addresspublicconstant EULER_ETOKEN_WSTETH =0xbd1bd5C956684f7EB79DA40f582cbE1373A1D593;
// Euler supply token for USDC (eUSDC)addresspublicconstant EULER_ETOKEN_USDC =0xEb91861f8A4e1C12333F42DCE8fB0Ecdc28dA716;
// Euler debt token wethaddresspublicconstant EULER_DTOKEN_WETH =0x62e28f054efc24b26A794F5C1249B6349454352C;
// address of the EULER rewards token contractaddresspublicconstant EULER_REWARDS_TOKEN =0xd9Fcd98c322942075A5C3860693e9f4f03AAE07b;
// adress of the Chainlink aggregator for the USDC/eth price feedaddresspublicconstant CHAINLINK_USDC_ETH_PRICE_FEED =0x986b5E1e1755e3C2440e960477f25201B0a8bbD4;
// Chainlink pricefeed (stETH -> ETH)addresspublicconstant CHAINLINK_STETH_ETH_PRICE_FEED =0x86392dC19c0b719886221c78AB11eb8Cf5c52812;
// Liquity pricefeed (USD -> ETH) with Chainlink as primary and Tellor as backup.addresspublicconstant LIQUITY_USD_ETH_PRICE_FEED =0x4c517D4e2C851CA76d7eC94B805269Df0f2201De;
// address of the Balancer vault contractaddresspublicconstant BALANCER_VAULT =0xBA12222222228d8Ba445958a75a0704d566BF2C8;
// Balancer admin accountaddresspublicconstant BALANCER_ADMIN =0x97207B095e4D5C9a6e4cfbfcd2C3358E03B90c4A;
// address of the Balance Protocol Fees Collector contractaddresspublicconstant BALANCER_FEES_COLLECTOR =0xce88686553686DA562CE7Cea497CE749DA109f9F;
// address of the 0x swap router contractaddresspublicconstant ZERO_EX_ROUTER =0xDef1C0ded9bec7F1a1670819833240f027b25EfF;
// Compound v3addresspublicconstant COMPOUND_V3_COMET_WETH =0xA17581A9E3356d9A858b789D68B4d866e593aE94;
// Aave v2 lending pooladdresspublicconstant AAVE_V2_LENDING_POOL =0x7d2768dE32b0b80b7a3454c06BdAc94A69DDc7A9;
// Aave v2 protocol data provideraddresspublicconstant AAVE_V2_PROTOCOL_DATA_PROVIDER =0x057835Ad21a177dbdd3090bB1CAE03EaCF78Fc6d;
// Aave v2 interest bearing USDC (aUSDC) tokenaddresspublicconstant AAVE_V2_AUSDC_TOKEN =0xBcca60bB61934080951369a648Fb03DF4F96263C;
// Aave v2 variable debt bearing WETH (variableDebtWETH) tokenaddresspublicconstant AAVE_V2_VAR_DEBT_WETH_TOKEN =0xF63B34710400CAd3e044cFfDcAb00a0f32E33eCf;
// Liquityaddresspublicconstant LIQUITY_STABILITY_POOL =0x66017D22b0f8556afDd19FC67041899Eb65a21bb;
addresspublicconstant LIQUITY_LQTY_TOKEN =0x6DEA81C8171D0bA574754EF6F8b412F2Ed88c54D;
// Morphoaddresspublicconstant MORPHO =0x33333aea097c193e66081E930c33020272b33333;
}
Contract Source Code
File 6 of 31: Context.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)pragmasolidity ^0.8.0;/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/abstractcontractContext{
function_msgSender() internalviewvirtualreturns (address) {
returnmsg.sender;
}
function_msgData() internalviewvirtualreturns (bytescalldata) {
returnmsg.data;
}
}
Contract Source Code
File 7 of 31: ERC165.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)pragmasolidity ^0.8.0;import"./IERC165.sol";
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
* for the additional interface id that will be supported. For example:
*
* ```solidity
* function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
* return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
* }
* ```
*
* Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
*/abstractcontractERC165isIERC165{
/**
* @dev See {IERC165-supportsInterface}.
*/functionsupportsInterface(bytes4 interfaceId) publicviewvirtualoverridereturns (bool) {
return interfaceId ==type(IERC165).interfaceId;
}
}
Contract Source Code
File 8 of 31: ERC20.sol
// SPDX-License-Identifier: AGPL-3.0-onlypragmasolidity >=0.8.0;/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation./// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.abstractcontractERC20{
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/eventTransfer(addressindexedfrom, addressindexed to, uint256 amount);
eventApproval(addressindexed owner, addressindexed spender, uint256 amount);
/*//////////////////////////////////////////////////////////////
METADATA STORAGE
//////////////////////////////////////////////////////////////*/stringpublic name;
stringpublic symbol;
uint8publicimmutable decimals;
/*//////////////////////////////////////////////////////////////
ERC20 STORAGE
//////////////////////////////////////////////////////////////*/uint256public totalSupply;
mapping(address=>uint256) public balanceOf;
mapping(address=>mapping(address=>uint256)) public allowance;
/*//////////////////////////////////////////////////////////////
EIP-2612 STORAGE
//////////////////////////////////////////////////////////////*/uint256internalimmutable INITIAL_CHAIN_ID;
bytes32internalimmutable INITIAL_DOMAIN_SEPARATOR;
mapping(address=>uint256) public nonces;
/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/constructor(stringmemory _name,
stringmemory _symbol,
uint8 _decimals
) {
name = _name;
symbol = _symbol;
decimals = _decimals;
INITIAL_CHAIN_ID =block.chainid;
INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
}
/*//////////////////////////////////////////////////////////////
ERC20 LOGIC
//////////////////////////////////////////////////////////////*/functionapprove(address spender, uint256 amount) publicvirtualreturns (bool) {
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
returntrue;
}
functiontransfer(address to, uint256 amount) publicvirtualreturns (bool) {
balanceOf[msg.sender] -= amount;
// Cannot overflow because the sum of all user// balances can't exceed the max uint256 value.unchecked {
balanceOf[to] += amount;
}
emit Transfer(msg.sender, to, amount);
returntrue;
}
functiontransferFrom(addressfrom,
address to,
uint256 amount
) publicvirtualreturns (bool) {
uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.if (allowed !=type(uint256).max) allowance[from][msg.sender] = allowed - amount;
balanceOf[from] -= amount;
// Cannot overflow because the sum of all user// balances can't exceed the max uint256 value.unchecked {
balanceOf[to] += amount;
}
emit Transfer(from, to, amount);
returntrue;
}
/*//////////////////////////////////////////////////////////////
EIP-2612 LOGIC
//////////////////////////////////////////////////////////////*/functionpermit(address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) publicvirtual{
require(deadline >=block.timestamp, "PERMIT_DEADLINE_EXPIRED");
// Unchecked because the only math done is incrementing// the owner's nonce which cannot realistically overflow.unchecked {
address recoveredAddress =ecrecover(
keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR(),
keccak256(
abi.encode(
keccak256(
"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
),
owner,
spender,
value,
nonces[owner]++,
deadline
)
)
)
),
v,
r,
s
);
require(recoveredAddress !=address(0) && recoveredAddress == owner, "INVALID_SIGNER");
allowance[recoveredAddress][spender] = value;
}
emit Approval(owner, spender, value);
}
functionDOMAIN_SEPARATOR() publicviewvirtualreturns (bytes32) {
returnblock.chainid== INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
}
functioncomputeDomainSeparator() internalviewvirtualreturns (bytes32) {
returnkeccak256(
abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256(bytes(name)),
keccak256("1"),
block.chainid,
address(this)
)
);
}
/*//////////////////////////////////////////////////////////////
INTERNAL MINT/BURN LOGIC
//////////////////////////////////////////////////////////////*/function_mint(address to, uint256 amount) internalvirtual{
totalSupply += amount;
// Cannot overflow because the sum of all user// balances can't exceed the max uint256 value.unchecked {
balanceOf[to] += amount;
}
emit Transfer(address(0), to, amount);
}
function_burn(addressfrom, uint256 amount) internalvirtual{
balanceOf[from] -= amount;
// Cannot underflow because a user's balance// will never be larger than the total supply.unchecked {
totalSupply -= amount;
}
emit Transfer(from, address(0), amount);
}
}
Contract Source Code
File 9 of 31: ERC4626.sol
// SPDX-License-Identifier: AGPL-3.0-onlypragmasolidity >=0.8.0;import {ERC20} from"../tokens/ERC20.sol";
import {SafeTransferLib} from"../utils/SafeTransferLib.sol";
import {FixedPointMathLib} from"../utils/FixedPointMathLib.sol";
/// @notice Minimal ERC4626 tokenized Vault implementation./// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/mixins/ERC4626.sol)abstractcontractERC4626isERC20{
usingSafeTransferLibforERC20;
usingFixedPointMathLibforuint256;
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/eventDeposit(addressindexed caller, addressindexed owner, uint256 assets, uint256 shares);
eventWithdraw(addressindexed caller,
addressindexed receiver,
addressindexed owner,
uint256 assets,
uint256 shares
);
/*//////////////////////////////////////////////////////////////
IMMUTABLES
//////////////////////////////////////////////////////////////*/
ERC20 publicimmutable asset;
constructor(
ERC20 _asset,
stringmemory _name,
stringmemory _symbol
) ERC20(_name, _symbol, _asset.decimals()) {
asset = _asset;
}
/*//////////////////////////////////////////////////////////////
DEPOSIT/WITHDRAWAL LOGIC
//////////////////////////////////////////////////////////////*/functiondeposit(uint256 assets, address receiver) publicvirtualreturns (uint256 shares) {
// Check for rounding error since we round down in previewDeposit.require((shares = previewDeposit(assets)) !=0, "ZERO_SHARES");
// Need to transfer before minting or ERC777s could reenter.
asset.safeTransferFrom(msg.sender, address(this), assets);
_mint(receiver, shares);
emit Deposit(msg.sender, receiver, assets, shares);
afterDeposit(assets, shares);
}
functionmint(uint256 shares, address receiver) publicvirtualreturns (uint256 assets) {
assets = previewMint(shares); // No need to check for rounding error, previewMint rounds up.// Need to transfer before minting or ERC777s could reenter.
asset.safeTransferFrom(msg.sender, address(this), assets);
_mint(receiver, shares);
emit Deposit(msg.sender, receiver, assets, shares);
afterDeposit(assets, shares);
}
functionwithdraw(uint256 assets,
address receiver,
address owner
) publicvirtualreturns (uint256 shares) {
shares = previewWithdraw(assets); // No need to check for rounding error, previewWithdraw rounds up.if (msg.sender!= owner) {
uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals.if (allowed !=type(uint256).max) allowance[owner][msg.sender] = allowed - shares;
}
beforeWithdraw(assets, shares);
_burn(owner, shares);
emit Withdraw(msg.sender, receiver, owner, assets, shares);
asset.safeTransfer(receiver, assets);
}
functionredeem(uint256 shares,
address receiver,
address owner
) publicvirtualreturns (uint256 assets) {
if (msg.sender!= owner) {
uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals.if (allowed !=type(uint256).max) allowance[owner][msg.sender] = allowed - shares;
}
// Check for rounding error since we round down in previewRedeem.require((assets = previewRedeem(shares)) !=0, "ZERO_ASSETS");
beforeWithdraw(assets, shares);
_burn(owner, shares);
emit Withdraw(msg.sender, receiver, owner, assets, shares);
asset.safeTransfer(receiver, assets);
}
/*//////////////////////////////////////////////////////////////
ACCOUNTING LOGIC
//////////////////////////////////////////////////////////////*/functiontotalAssets() publicviewvirtualreturns (uint256);
functionconvertToShares(uint256 assets) publicviewvirtualreturns (uint256) {
uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero.return supply ==0 ? assets : assets.mulDivDown(supply, totalAssets());
}
functionconvertToAssets(uint256 shares) publicviewvirtualreturns (uint256) {
uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero.return supply ==0 ? shares : shares.mulDivDown(totalAssets(), supply);
}
functionpreviewDeposit(uint256 assets) publicviewvirtualreturns (uint256) {
return convertToShares(assets);
}
functionpreviewMint(uint256 shares) publicviewvirtualreturns (uint256) {
uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero.return supply ==0 ? shares : shares.mulDivUp(totalAssets(), supply);
}
functionpreviewWithdraw(uint256 assets) publicviewvirtualreturns (uint256) {
uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero.return supply ==0 ? assets : assets.mulDivUp(supply, totalAssets());
}
functionpreviewRedeem(uint256 shares) publicviewvirtualreturns (uint256) {
return convertToAssets(shares);
}
/*//////////////////////////////////////////////////////////////
DEPOSIT/WITHDRAWAL LIMIT LOGIC
//////////////////////////////////////////////////////////////*/functionmaxDeposit(address) publicviewvirtualreturns (uint256) {
returntype(uint256).max;
}
functionmaxMint(address) publicviewvirtualreturns (uint256) {
returntype(uint256).max;
}
functionmaxWithdraw(address owner) publicviewvirtualreturns (uint256) {
return convertToAssets(balanceOf[owner]);
}
functionmaxRedeem(address owner) publicviewvirtualreturns (uint256) {
return balanceOf[owner];
}
/*//////////////////////////////////////////////////////////////
INTERNAL HOOKS LOGIC
//////////////////////////////////////////////////////////////*/functionbeforeWithdraw(uint256 assets, uint256 shares) internalvirtual{}
functionafterDeposit(uint256 assets, uint256 shares) internalvirtual{}
}
Contract Source Code
File 10 of 31: EnumerableMap.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v4.8.0) (utils/structs/EnumerableMap.sol)// This file was procedurally generated from scripts/generate/templates/EnumerableMap.js.pragmasolidity ^0.8.0;import"./EnumerableSet.sol";
/**
* @dev Library for managing an enumerable variant of Solidity's
* https://solidity.readthedocs.io/en/latest/types.html#mapping-types[`mapping`]
* type.
*
* Maps have the following properties:
*
* - Entries are added, removed, and checked for existence in constant time
* (O(1)).
* - Entries are enumerated in O(n). No guarantees are made on the ordering.
*
* ```
* contract Example {
* // Add the library methods
* using EnumerableMap for EnumerableMap.UintToAddressMap;
*
* // Declare a set state variable
* EnumerableMap.UintToAddressMap private myMap;
* }
* ```
*
* The following map types are supported:
*
* - `uint256 -> address` (`UintToAddressMap`) since v3.0.0
* - `address -> uint256` (`AddressToUintMap`) since v4.6.0
* - `bytes32 -> bytes32` (`Bytes32ToBytes32Map`) since v4.6.0
* - `uint256 -> uint256` (`UintToUintMap`) since v4.7.0
* - `bytes32 -> uint256` (`Bytes32ToUintMap`) since v4.7.0
*
* [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 EnumerableMap, you can either remove all elements one by one or create a fresh instance using an
* array of EnumerableMap.
* ====
*/libraryEnumerableMap{
usingEnumerableSetforEnumerableSet.Bytes32Set;
// To implement this library for multiple types with as little code// repetition as possible, we write it in terms of a generic Map type with// bytes32 keys and values.// The Map implementation uses private functions, and user-facing// implementations (such as Uint256ToAddressMap) are just wrappers around// the underlying Map.// This means that we can only create new EnumerableMaps for types that fit// in bytes32.structBytes32ToBytes32Map {
// Storage of keys
EnumerableSet.Bytes32Set _keys;
mapping(bytes32=>bytes32) _values;
}
/**
* @dev Adds a key-value pair to a map, or updates the value for an existing
* key. O(1).
*
* Returns true if the key was added to the map, that is if it was not
* already present.
*/functionset(
Bytes32ToBytes32Map storage map,
bytes32 key,
bytes32 value
) internalreturns (bool) {
map._values[key] = value;
return map._keys.add(key);
}
/**
* @dev Removes a key-value pair from a map. O(1).
*
* Returns true if the key was removed from the map, that is if it was present.
*/functionremove(Bytes32ToBytes32Map storage map, bytes32 key) internalreturns (bool) {
delete map._values[key];
return map._keys.remove(key);
}
/**
* @dev Returns true if the key is in the map. O(1).
*/functioncontains(Bytes32ToBytes32Map storage map, bytes32 key) internalviewreturns (bool) {
return map._keys.contains(key);
}
/**
* @dev Returns the number of key-value pairs in the map. O(1).
*/functionlength(Bytes32ToBytes32Map storage map) internalviewreturns (uint256) {
return map._keys.length();
}
/**
* @dev Returns the key-value pair stored at position `index` in the map. O(1).
*
* Note that there are no guarantees on the ordering of entries inside the
* array, and it may change when more entries are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/functionat(Bytes32ToBytes32Map storage map, uint256 index) internalviewreturns (bytes32, bytes32) {
bytes32 key = map._keys.at(index);
return (key, map._values[key]);
}
/**
* @dev Tries to returns the value associated with `key`. O(1).
* Does not revert if `key` is not in the map.
*/functiontryGet(Bytes32ToBytes32Map storage map, bytes32 key) internalviewreturns (bool, bytes32) {
bytes32 value = map._values[key];
if (value ==bytes32(0)) {
return (contains(map, key), bytes32(0));
} else {
return (true, value);
}
}
/**
* @dev Returns the value associated with `key`. O(1).
*
* Requirements:
*
* - `key` must be in the map.
*/functionget(Bytes32ToBytes32Map storage map, bytes32 key) internalviewreturns (bytes32) {
bytes32 value = map._values[key];
require(value !=0|| contains(map, key), "EnumerableMap: nonexistent key");
return value;
}
/**
* @dev Same as {get}, with a custom error message when `key` is not in the map.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {tryGet}.
*/functionget(
Bytes32ToBytes32Map storage map,
bytes32 key,
stringmemory errorMessage
) internalviewreturns (bytes32) {
bytes32 value = map._values[key];
require(value !=0|| contains(map, key), errorMessage);
return value;
}
// UintToUintMapstructUintToUintMap {
Bytes32ToBytes32Map _inner;
}
/**
* @dev Adds a key-value pair to a map, or updates the value for an existing
* key. O(1).
*
* Returns true if the key was added to the map, that is if it was not
* already present.
*/functionset(
UintToUintMap storage map,
uint256 key,
uint256 value
) internalreturns (bool) {
return set(map._inner, bytes32(key), bytes32(value));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the key was removed from the map, that is if it was present.
*/functionremove(UintToUintMap storage map, uint256 key) internalreturns (bool) {
return remove(map._inner, bytes32(key));
}
/**
* @dev Returns true if the key is in the map. O(1).
*/functioncontains(UintToUintMap storage map, uint256 key) internalviewreturns (bool) {
return contains(map._inner, bytes32(key));
}
/**
* @dev Returns the number of elements in the map. O(1).
*/functionlength(UintToUintMap storage map) internalviewreturns (uint256) {
return length(map._inner);
}
/**
* @dev Returns the element 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}.
*/functionat(UintToUintMap storage map, uint256 index) internalviewreturns (uint256, uint256) {
(bytes32 key, bytes32 value) = at(map._inner, index);
return (uint256(key), uint256(value));
}
/**
* @dev Tries to returns the value associated with `key`. O(1).
* Does not revert if `key` is not in the map.
*/functiontryGet(UintToUintMap storage map, uint256 key) internalviewreturns (bool, uint256) {
(bool success, bytes32 value) = tryGet(map._inner, bytes32(key));
return (success, uint256(value));
}
/**
* @dev Returns the value associated with `key`. O(1).
*
* Requirements:
*
* - `key` must be in the map.
*/functionget(UintToUintMap storage map, uint256 key) internalviewreturns (uint256) {
returnuint256(get(map._inner, bytes32(key)));
}
/**
* @dev Same as {get}, with a custom error message when `key` is not in the map.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {tryGet}.
*/functionget(
UintToUintMap storage map,
uint256 key,
stringmemory errorMessage
) internalviewreturns (uint256) {
returnuint256(get(map._inner, bytes32(key), errorMessage));
}
// UintToAddressMapstructUintToAddressMap {
Bytes32ToBytes32Map _inner;
}
/**
* @dev Adds a key-value pair to a map, or updates the value for an existing
* key. O(1).
*
* Returns true if the key was added to the map, that is if it was not
* already present.
*/functionset(
UintToAddressMap storage map,
uint256 key,
address value
) internalreturns (bool) {
return set(map._inner, bytes32(key), bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the key was removed from the map, that is if it was present.
*/functionremove(UintToAddressMap storage map, uint256 key) internalreturns (bool) {
return remove(map._inner, bytes32(key));
}
/**
* @dev Returns true if the key is in the map. O(1).
*/functioncontains(UintToAddressMap storage map, uint256 key) internalviewreturns (bool) {
return contains(map._inner, bytes32(key));
}
/**
* @dev Returns the number of elements in the map. O(1).
*/functionlength(UintToAddressMap storage map) internalviewreturns (uint256) {
return length(map._inner);
}
/**
* @dev Returns the element 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}.
*/functionat(UintToAddressMap storage map, uint256 index) internalviewreturns (uint256, address) {
(bytes32 key, bytes32 value) = at(map._inner, index);
return (uint256(key), address(uint160(uint256(value))));
}
/**
* @dev Tries to returns the value associated with `key`. O(1).
* Does not revert if `key` is not in the map.
*/functiontryGet(UintToAddressMap storage map, uint256 key) internalviewreturns (bool, address) {
(bool success, bytes32 value) = tryGet(map._inner, bytes32(key));
return (success, address(uint160(uint256(value))));
}
/**
* @dev Returns the value associated with `key`. O(1).
*
* Requirements:
*
* - `key` must be in the map.
*/functionget(UintToAddressMap storage map, uint256 key) internalviewreturns (address) {
returnaddress(uint160(uint256(get(map._inner, bytes32(key)))));
}
/**
* @dev Same as {get}, with a custom error message when `key` is not in the map.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {tryGet}.
*/functionget(
UintToAddressMap storage map,
uint256 key,
stringmemory errorMessage
) internalviewreturns (address) {
returnaddress(uint160(uint256(get(map._inner, bytes32(key), errorMessage))));
}
// AddressToUintMapstructAddressToUintMap {
Bytes32ToBytes32Map _inner;
}
/**
* @dev Adds a key-value pair to a map, or updates the value for an existing
* key. O(1).
*
* Returns true if the key was added to the map, that is if it was not
* already present.
*/functionset(
AddressToUintMap storage map,
address key,
uint256 value
) internalreturns (bool) {
return set(map._inner, bytes32(uint256(uint160(key))), bytes32(value));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the key was removed from the map, that is if it was present.
*/functionremove(AddressToUintMap storage map, address key) internalreturns (bool) {
return remove(map._inner, bytes32(uint256(uint160(key))));
}
/**
* @dev Returns true if the key is in the map. O(1).
*/functioncontains(AddressToUintMap storage map, address key) internalviewreturns (bool) {
return contains(map._inner, bytes32(uint256(uint160(key))));
}
/**
* @dev Returns the number of elements in the map. O(1).
*/functionlength(AddressToUintMap storage map) internalviewreturns (uint256) {
return length(map._inner);
}
/**
* @dev Returns the element 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}.
*/functionat(AddressToUintMap storage map, uint256 index) internalviewreturns (address, uint256) {
(bytes32 key, bytes32 value) = at(map._inner, index);
return (address(uint160(uint256(key))), uint256(value));
}
/**
* @dev Tries to returns the value associated with `key`. O(1).
* Does not revert if `key` is not in the map.
*/functiontryGet(AddressToUintMap storage map, address key) internalviewreturns (bool, uint256) {
(bool success, bytes32 value) = tryGet(map._inner, bytes32(uint256(uint160(key))));
return (success, uint256(value));
}
/**
* @dev Returns the value associated with `key`. O(1).
*
* Requirements:
*
* - `key` must be in the map.
*/functionget(AddressToUintMap storage map, address key) internalviewreturns (uint256) {
returnuint256(get(map._inner, bytes32(uint256(uint160(key)))));
}
/**
* @dev Same as {get}, with a custom error message when `key` is not in the map.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {tryGet}.
*/functionget(
AddressToUintMap storage map,
address key,
stringmemory errorMessage
) internalviewreturns (uint256) {
returnuint256(get(map._inner, bytes32(uint256(uint160(key))), errorMessage));
}
// Bytes32ToUintMapstructBytes32ToUintMap {
Bytes32ToBytes32Map _inner;
}
/**
* @dev Adds a key-value pair to a map, or updates the value for an existing
* key. O(1).
*
* Returns true if the key was added to the map, that is if it was not
* already present.
*/functionset(
Bytes32ToUintMap storage map,
bytes32 key,
uint256 value
) internalreturns (bool) {
return set(map._inner, key, bytes32(value));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the key was removed from the map, that is if it was present.
*/functionremove(Bytes32ToUintMap storage map, bytes32 key) internalreturns (bool) {
return remove(map._inner, key);
}
/**
* @dev Returns true if the key is in the map. O(1).
*/functioncontains(Bytes32ToUintMap storage map, bytes32 key) internalviewreturns (bool) {
return contains(map._inner, key);
}
/**
* @dev Returns the number of elements in the map. O(1).
*/functionlength(Bytes32ToUintMap storage map) internalviewreturns (uint256) {
return length(map._inner);
}
/**
* @dev Returns the element 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}.
*/functionat(Bytes32ToUintMap storage map, uint256 index) internalviewreturns (bytes32, uint256) {
(bytes32 key, bytes32 value) = at(map._inner, index);
return (key, uint256(value));
}
/**
* @dev Tries to returns the value associated with `key`. O(1).
* Does not revert if `key` is not in the map.
*/functiontryGet(Bytes32ToUintMap storage map, bytes32 key) internalviewreturns (bool, uint256) {
(bool success, bytes32 value) = tryGet(map._inner, key);
return (success, uint256(value));
}
/**
* @dev Returns the value associated with `key`. O(1).
*
* Requirements:
*
* - `key` must be in the map.
*/functionget(Bytes32ToUintMap storage map, bytes32 key) internalviewreturns (uint256) {
returnuint256(get(map._inner, key));
}
/**
* @dev Same as {get}, with a custom error message when `key` is not in the map.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {tryGet}.
*/functionget(
Bytes32ToUintMap storage map,
bytes32 key,
stringmemory errorMessage
) internalviewreturns (uint256) {
returnuint256(get(map._inner, key, errorMessage));
}
}
Contract Source Code
File 11 of 31: EnumerableSet.sol
// 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.pragmasolidity ^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.
* ====
*/libraryEnumerableSet{
// 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.structSet {
// Storage of set valuesbytes32[] _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) privatereturns (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;
returntrue;
} else {
returnfalse;
}
}
/**
* @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) privatereturns (bool) {
// We read and store the value's index to prevent multiple reads from the same storage slotuint256 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 slotdelete set._indexes[value];
returntrue;
} else {
returnfalse;
}
}
/**
* @dev Returns true if the value is in the set. O(1).
*/function_contains(Set storage set, bytes32 value) privateviewreturns (bool) {
return set._indexes[value] !=0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/function_length(Set storage set) privateviewreturns (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) privateviewreturns (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) privateviewreturns (bytes32[] memory) {
return set._values;
}
// Bytes32SetstructBytes32Set {
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.
*/functionadd(Bytes32Set storage set, bytes32 value) internalreturns (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.
*/functionremove(Bytes32Set storage set, bytes32 value) internalreturns (bool) {
return _remove(set._inner, value);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/functioncontains(Bytes32Set storage set, bytes32 value) internalviewreturns (bool) {
return _contains(set._inner, value);
}
/**
* @dev Returns the number of values in the set. O(1).
*/functionlength(Bytes32Set storage set) internalviewreturns (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}.
*/functionat(Bytes32Set storage set, uint256 index) internalviewreturns (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.
*/functionvalues(Bytes32Set storage set) internalviewreturns (bytes32[] memory) {
bytes32[] memory store = _values(set._inner);
bytes32[] memory result;
/// @solidity memory-safe-assemblyassembly {
result := store
}
return result;
}
// AddressSetstructAddressSet {
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.
*/functionadd(AddressSet storage set, address value) internalreturns (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.
*/functionremove(AddressSet storage set, address value) internalreturns (bool) {
return _remove(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/functioncontains(AddressSet storage set, address value) internalviewreturns (bool) {
return _contains(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns the number of values in the set. O(1).
*/functionlength(AddressSet storage set) internalviewreturns (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}.
*/functionat(AddressSet storage set, uint256 index) internalviewreturns (address) {
returnaddress(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.
*/functionvalues(AddressSet storage set) internalviewreturns (address[] memory) {
bytes32[] memory store = _values(set._inner);
address[] memory result;
/// @solidity memory-safe-assemblyassembly {
result := store
}
return result;
}
// UintSetstructUintSet {
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.
*/functionadd(UintSet storage set, uint256 value) internalreturns (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.
*/functionremove(UintSet storage set, uint256 value) internalreturns (bool) {
return _remove(set._inner, bytes32(value));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/functioncontains(UintSet storage set, uint256 value) internalviewreturns (bool) {
return _contains(set._inner, bytes32(value));
}
/**
* @dev Returns the number of values in the set. O(1).
*/functionlength(UintSet storage set) internalviewreturns (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}.
*/functionat(UintSet storage set, uint256 index) internalviewreturns (uint256) {
returnuint256(_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.
*/functionvalues(UintSet storage set) internalviewreturns (uint256[] memory) {
bytes32[] memory store = _values(set._inner);
uint256[] memory result;
/// @solidity memory-safe-assemblyassembly {
result := store
}
return result;
}
}
Contract Source Code
File 12 of 31: FixedPointMathLib.sol
// SPDX-License-Identifier: AGPL-3.0-onlypragmasolidity >=0.8.0;/// @notice Arithmetic library with operations for fixed-point numbers./// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol)/// @author Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol)libraryFixedPointMathLib{
/*//////////////////////////////////////////////////////////////
SIMPLIFIED FIXED POINT OPERATIONS
//////////////////////////////////////////////////////////////*/uint256internalconstant MAX_UINT256 =2**256-1;
uint256internalconstant WAD =1e18; // The scalar of ETH and most ERC20s.functionmulWadDown(uint256 x, uint256 y) internalpurereturns (uint256) {
return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down.
}
functionmulWadUp(uint256 x, uint256 y) internalpurereturns (uint256) {
return mulDivUp(x, y, WAD); // Equivalent to (x * y) / WAD rounded up.
}
functiondivWadDown(uint256 x, uint256 y) internalpurereturns (uint256) {
return mulDivDown(x, WAD, y); // Equivalent to (x * WAD) / y rounded down.
}
functiondivWadUp(uint256 x, uint256 y) internalpurereturns (uint256) {
return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up.
}
/*//////////////////////////////////////////////////////////////
LOW LEVEL FIXED POINT OPERATIONS
//////////////////////////////////////////////////////////////*/functionmulDivDown(uint256 x,
uint256 y,
uint256 denominator
) internalpurereturns (uint256 z) {
/// @solidity memory-safe-assemblyassembly {
// Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))ifiszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
revert(0, 0)
}
// Divide x * y by the denominator.
z :=div(mul(x, y), denominator)
}
}
functionmulDivUp(uint256 x,
uint256 y,
uint256 denominator
) internalpurereturns (uint256 z) {
/// @solidity memory-safe-assemblyassembly {
// Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))ifiszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
revert(0, 0)
}
// If x * y modulo the denominator is strictly greater than 0,// 1 is added to round up the division of x * y by the denominator.
z :=add(gt(mod(mul(x, y), denominator), 0), div(mul(x, y), denominator))
}
}
functionrpow(uint256 x,
uint256 n,
uint256 scalar
) internalpurereturns (uint256 z) {
/// @solidity memory-safe-assemblyassembly {
switch x
case0 {
switch n
case0 {
// 0 ** 0 = 1
z := scalar
}
default {
// 0 ** n = 0
z :=0
}
}
default {
switchmod(n, 2)
case0 {
// If n is even, store scalar in z for now.
z := scalar
}
default {
// If n is odd, store x in z for now.
z := x
}
// Shifting right by 1 is like dividing by 2.let half :=shr(1, scalar)
for {
// Shift n right by 1 before looping to halve it.
n :=shr(1, n)
} n {
// Shift n right by 1 each iteration to halve it.
n :=shr(1, n)
} {
// Revert immediately if x ** 2 would overflow.// Equivalent to iszero(eq(div(xx, x), x)) here.ifshr(128, x) {
revert(0, 0)
}
// Store x squared.let xx :=mul(x, x)
// Round to the nearest number.let xxRound :=add(xx, half)
// Revert if xx + half overflowed.iflt(xxRound, xx) {
revert(0, 0)
}
// Set x to scaled xxRound.
x :=div(xxRound, scalar)
// If n is even:ifmod(n, 2) {
// Compute z * x.let zx :=mul(z, x)
// If z * x overflowed:ifiszero(eq(div(zx, x), z)) {
// Revert if x is non-zero.ifiszero(iszero(x)) {
revert(0, 0)
}
}
// Round to the nearest number.let zxRound :=add(zx, half)
// Revert if zx + half overflowed.iflt(zxRound, zx) {
revert(0, 0)
}
// Return properly scaled zxRound.
z :=div(zxRound, scalar)
}
}
}
}
}
/*//////////////////////////////////////////////////////////////
GENERAL NUMBER UTILITIES
//////////////////////////////////////////////////////////////*/functionsqrt(uint256 x) internalpurereturns (uint256 z) {
/// @solidity memory-safe-assemblyassembly {
let y := x // We start y at x, which will help us make our initial estimate.
z :=181// The "correct" value is 1, but this saves a multiplication later.// This segment is to get a reasonable initial estimate for the Babylonian method. With a bad// start, the correct # of bits increases ~linearly each iteration instead of ~quadratically.// We check y >= 2^(k + 8) but shift right by k bits// each branch to ensure that if x >= 256, then y >= 256.ifiszero(lt(y, 0x10000000000000000000000000000000000)) {
y :=shr(128, y)
z :=shl(64, z)
}
ifiszero(lt(y, 0x1000000000000000000)) {
y :=shr(64, y)
z :=shl(32, z)
}
ifiszero(lt(y, 0x10000000000)) {
y :=shr(32, y)
z :=shl(16, z)
}
ifiszero(lt(y, 0x1000000)) {
y :=shr(16, y)
z :=shl(8, z)
}
// Goal was to get z*z*y within a small factor of x. More iterations could// get y in a tighter range. Currently, we will have y in [256, 256*2^16).// We ensured y >= 256 so that the relative difference between y and y+1 is small.// That's not possible if x < 256 but we can just verify those cases exhaustively.// Now, z*z*y <= x < z*z*(y+1), and y <= 2^(16+8), and either y >= 256, or x < 256.// Correctness can be checked exhaustively for x < 256, so we assume y >= 256.// Then z*sqrt(y) is within sqrt(257)/sqrt(256) of sqrt(x), or about 20bps.// For s in the range [1/256, 256], the estimate f(s) = (181/1024) * (s+1) is in the range// (1/2.84 * sqrt(s), 2.84 * sqrt(s)), with largest error when s = 1 and when s = 256 or 1/256.// Since y is in [256, 256*2^16), let a = y/65536, so that a is in [1/256, 256). Then we can estimate// sqrt(y) using sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2^18.// There is no overflow risk here since y < 2^136 after the first branch above.
z :=shr(18, mul(z, add(y, 65536))) // A mul() is saved from starting z at 181.// Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough.
z :=shr(1, add(z, div(x, z)))
z :=shr(1, add(z, div(x, z)))
z :=shr(1, add(z, div(x, z)))
z :=shr(1, add(z, div(x, z)))
z :=shr(1, add(z, div(x, z)))
z :=shr(1, add(z, div(x, z)))
z :=shr(1, add(z, div(x, z)))
// If x+1 is a perfect square, the Babylonian method cycles between// floor(sqrt(x)) and ceil(sqrt(x)). This statement ensures we return floor.// See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division// Since the ceil is rare, we save gas on the assignment and repeat division in the rare case.// If you don't care whether the floor or ceil square root is returned, you can remove this statement.
z :=sub(z, lt(div(x, z), z))
}
}
functionunsafeMod(uint256 x, uint256 y) internalpurereturns (uint256 z) {
/// @solidity memory-safe-assemblyassembly {
// Mod x by y. Note this will return// 0 instead of reverting if y is zero.
z :=mod(x, y)
}
}
functionunsafeDiv(uint256 x, uint256 y) internalpurereturns (uint256 r) {
/// @solidity memory-safe-assemblyassembly {
// Divide x by y. Note this will return// 0 instead of reverting if y is zero.
r :=div(x, y)
}
}
functionunsafeDivUp(uint256 x, uint256 y) internalpurereturns (uint256 z) {
/// @solidity memory-safe-assemblyassembly {
// Add 1 to x * y if x % y > 0. Note this will// return 0 instead of reverting if y is zero.
z :=add(gt(mod(x, y), 0), div(x, y))
}
}
}
Contract Source Code
File 13 of 31: IAccessControl.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)pragmasolidity ^0.8.0;/**
* @dev External interface of AccessControl declared to support ERC165 detection.
*/interfaceIAccessControl{
/**
* @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
*
* `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
* {RoleAdminChanged} not being emitted signaling this.
*
* _Available since v3.1._
*/eventRoleAdminChanged(bytes32indexed role, bytes32indexed previousAdminRole, bytes32indexed newAdminRole);
/**
* @dev Emitted when `account` is granted `role`.
*
* `sender` is the account that originated the contract call, an admin role
* bearer except when using {AccessControl-_setupRole}.
*/eventRoleGranted(bytes32indexed role, addressindexed account, addressindexed sender);
/**
* @dev Emitted when `account` is revoked `role`.
*
* `sender` is the account that originated the contract call:
* - if using `revokeRole`, it is the admin role bearer
* - if using `renounceRole`, it is the role bearer (i.e. `account`)
*/eventRoleRevoked(bytes32indexed role, addressindexed account, addressindexed sender);
/**
* @dev Returns `true` if `account` has been granted `role`.
*/functionhasRole(bytes32 role, address account) externalviewreturns (bool);
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {AccessControl-_setRoleAdmin}.
*/functiongetRoleAdmin(bytes32 role) externalviewreturns (bytes32);
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/functiongrantRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/functionrevokeRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been granted `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `account`.
*/functionrenounceRole(bytes32 role, address account) external;
}
Contract Source Code
File 14 of 31: IAdapter.sol
// SPDX-License-Identifier: AGPL-3.0-onlypragmasolidity ^0.8.19;/**
* @notice Interface for adapters that allow interactions with the lending protocols
*/interfaceIAdapter{
/**
* @notice Returns the adapter's ID
*/functionid() externalviewreturns (uint256);
/**
* @notice Sets the necessary approvals (allowances) for interacting with the lending protocol
*/functionsetApprovals() external;
/**
* @notice Removes the given approvals (allowances) for interacting with the lending protocol
*/functionrevokeApprovals() external;
/**
* @notice Supplies the given amount of collateral to the lending protocol
* @param amount The amount of collateral to supply
*/functionsupply(uint256 amount) external;
/**
* @notice Borrows the given amount of debt from the lending protocol
* @param amount The amount of debt to borrow
*/functionborrow(uint256 amount) external;
/**
* @notice Repays the given amount of debt to the lending protocol
* @param amount The amount of debt to repay
*/functionrepay(uint256 amount) external;
/**
* @notice Withdraws the given amount of collateral from the lending protocol
* @param amount The amount of collateral to withdraw
*/functionwithdraw(uint256 amount) external;
/**
* @notice Claims rewards awarded by the lending protocol
* @param data Any data needed for the claim process
*/functionclaimRewards(bytescalldata data) external;
/**
* @notice Returns the amount of collateral currently supplied to the lending protocol
* @param account The account to check
*/functiongetCollateral(address account) externalviewreturns (uint256);
/**
* @notice Returns the amount of debt currently borrowed from the lending protocol
* @param account The account to check
*/functiongetDebt(address account) externalviewreturns (uint256);
/**
* @notice Returns the maximum loan-to-value (LTV) ratio for the lending protocol
*/functiongetMaxLtv() externalviewreturns (uint256);
}
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)pragmasolidity ^0.8.0;/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/interfaceIERC165{
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/functionsupportsInterface(bytes4 interfaceId) externalviewreturns (bool);
}
Contract Source Code
File 17 of 31: IERC20.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)pragmasolidity ^0.8.0;/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/interfaceIERC20{
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/eventTransfer(addressindexedfrom, addressindexed 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.
*/eventApproval(addressindexed owner, addressindexed spender, uint256 value);
/**
* @dev Returns the amount of tokens in existence.
*/functiontotalSupply() externalviewreturns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/functionbalanceOf(address account) externalviewreturns (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.
*/functiontransfer(address to, uint256 amount) externalreturns (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.
*/functionallowance(address owner, address spender) externalviewreturns (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.
*/functionapprove(address spender, uint256 amount) externalreturns (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.
*/functiontransferFrom(addressfrom,
address to,
uint256 amount
) externalreturns (bool);
}
Contract Source Code
File 18 of 31: IFlashLoanRecipient.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.0;// Inspired by Aave Protocol's IFlashLoanReceiver.interfaceIFlashLoanRecipient{
/**
* @dev When `flashLoan` is called on the Vault, it invokes the `receiveFlashLoan` hook on the recipient.
*
* At the time of the call, the Vault will have transferred `amounts` for `tokens` to the recipient. Before this
* call returns, the recipient must have transferred `amounts` plus `feeAmounts` for each token back to the
* Vault, or else the entire flash loan will revert.
*
* `userData` is the same value passed in the `IVault.flashLoan` call.
*/functionreceiveFlashLoan(address[] memory tokens,
uint256[] memory amounts,
uint256[] memory feeAmounts,
bytesmemory userData
) external;
}
Contract Source Code
File 19 of 31: ILido.sol
// SPDX-License-Identifier: AGPL-3.0-onlypragmasolidity ^0.8.10;import {IERC20} from"openzeppelin-contracts/token/ERC20/IERC20.sol";
interfaceILidoisIERC20{
functionsubmit(address _referral) externalpayablereturns (uint256);
/**
* @return the entire amount of Ether controlled by the protocol.
*
* @dev The sum of all ETH balances in the protocol, equals to the total supply of stETH.
*/functiongetTotalPooledEther() externalviewreturns (uint256);
}
Contract Source Code
File 20 of 31: ISwapRouter.sol
// SPDX-License-Identifier: AGPL-3.0-onlypragmasolidity ^0.8.13;/// @title Router token swapping functionality/// @notice Functions for swapping tokens via Uniswap V3interfaceISwapRouter{
structExactInputSingleParams {
address tokenIn;
address tokenOut;
uint24 fee;
address recipient;
uint256 deadline;
uint256 amountIn;
uint256 amountOutMinimum;
uint160 sqrtPriceLimitX96;
}
/// @notice Swaps `amountIn` of one token for as much as possible of another token/// @param params The parameters necessary for the swap, encoded as `ExactInputSingleParams` in calldata/// @return amountOut The amount of the received tokenfunctionexactInputSingle(ExactInputSingleParams calldata params) externalpayablereturns (uint256 amountOut);
structExactOutputSingleParams {
address tokenIn;
address tokenOut;
uint24 fee;
address recipient;
uint256 deadline;
uint256 amountOut;
uint256 amountInMaximum;
uint160 sqrtPriceLimitX96;
}
/// @notice Swaps as little as possible of one token for `amountOut` of another token/// @param params The parameters necessary for the swap, encoded as `ExactOutputSingleParams` in calldata/// @return amountIn The amount of the input tokenfunctionexactOutputSingle(ExactOutputSingleParams calldata params) externalpayablereturns (uint256 amountIn);
}
Contract Source Code
File 21 of 31: IVault.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.0;interfaceIVault{
/**
* @dev Performs a 'flash loan', sending tokens to `recipient`, executing the `receiveFlashLoan` hook on it,
* and then reverting unless the tokens plus a proportional protocol fee have been returned.
*
* The `tokens` and `amounts` arrays must have the same length, and each entry in these indicates the loan amount
* for each token contract. `tokens` must be sorted in ascending order.
*
* The 'userData' field is ignored by the Vault, and forwarded as-is to `recipient` as part of the
* `receiveFlashLoan` call.
*
* Emits `FlashLoan` events.
*/functionflashLoan(address recipient, address[] memory tokens, uint256[] memory amounts, bytesmemory userData)
external;
}
Contract Source Code
File 22 of 31: IwstETH.sol
// SPDX-License-Identifier: AGPL-3.0-onlypragmasolidity ^0.8.10;import {IERC20} from"openzeppelin-contracts/token/ERC20/IERC20.sol";
interfaceIwstETHisIERC20{
functionwrap(uint256 _stETHAmount) externalreturns (uint256);
/**
* @notice Exchanges wstETH to stETH
* @param _wstETHAmount amount of wstETH to uwrap in exchange for stETH
* @dev Requirements:
* - `_wstETHAmount` must be non-zero
* - msg.sender must have at least `_wstETHAmount` wstETH.
* @return Amount of stETH user receives after unwrap
*/functionunwrap(uint256 _wstETHAmount) externalreturns (uint256);
/**
* @notice Get amount of wstETH for a given amount of stETH
* @param _stETHAmount amount of stETH
* @return Amount of wstETH for a given stETH amount
*/functiongetWstETHByStETH(uint256 _stETHAmount) externalviewreturns (uint256);
/**
* @notice Get amount of stETH for a given amount of wstETH
* @param _wstETHAmount amount of wstETH
* @return Amount of stETH for a given wstETH amount
*/functiongetStETHByWstETH(uint256 _wstETHAmount) externalviewreturns (uint256);
/**
* @notice Get amount of stETH for a one wstETH
* @return Amount of stETH for 1 wstETH
*/functionstEthPerToken() externalviewreturns (uint256);
/**
* @notice Get amount of wstETH for a one stETH
* @return Amount of wstETH for a 1 stETH
*/functiontokensPerStEth() externalviewreturns (uint256);
}
Contract Source Code
File 23 of 31: Math.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol)pragmasolidity ^0.8.0;/**
* @dev Standard math utilities missing in the Solidity language.
*/libraryMath{
enumRounding {
Down, // Toward negative infinity
Up, // Toward infinity
Zero // Toward zero
}
/**
* @dev Returns the largest of two numbers.
*/functionmax(uint256 a, uint256 b) internalpurereturns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/functionmin(uint256 a, uint256 b) internalpurereturns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/functionaverage(uint256 a, uint256 b) internalpurereturns (uint256) {
// (a + b) / 2 can overflow.return (a & b) + (a ^ b) /2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds up instead
* of rounding down.
*/functionceilDiv(uint256 a, uint256 b) internalpurereturns (uint256) {
// (a + b - 1) / b can overflow on addition, so we distribute.return a ==0 ? 0 : (a -1) / b +1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
* with further edits by Uniswap Labs also under MIT license.
*/functionmulDiv(uint256 x,
uint256 y,
uint256 denominator
) internalpurereturns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256// variables such that product = prod1 * 2^256 + prod0.uint256 prod0; // Least significant 256 bits of the productuint256 prod1; // Most significant 256 bits of the productassembly {
let mm :=mulmod(x, y, not(0))
prod0 :=mul(x, y)
prod1 :=sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.if (prod1 ==0) {
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.require(denominator > prod1);
///////////////////////////////////////////////// 512 by 256 division.///////////////////////////////////////////////// Make division exact by subtracting the remainder from [prod1 prod0].uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder :=mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 :=sub(prod1, gt(remainder, prod0))
prod0 :=sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.// See https://cs.stackexchange.com/q/138556/92363.// Does not overflow because the denominator cannot be zero at this stage in the function.uint256 twos = denominator & (~denominator +1);
assembly {
// Divide denominator by twos.
denominator :=div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 :=div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos :=add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for// four bits. That is, denominator * inv = 1 mod 2^4.uint256 inverse = (3* denominator) ^2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works// in modular arithmetic, doubling the correct bits in each step.
inverse *=2- denominator * inverse; // inverse mod 2^8
inverse *=2- denominator * inverse; // inverse mod 2^16
inverse *=2- denominator * inverse; // inverse mod 2^32
inverse *=2- denominator * inverse; // inverse mod 2^64
inverse *=2- denominator * inverse; // inverse mod 2^128
inverse *=2- denominator * inverse; // inverse mod 2^256// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/functionmulDiv(uint256 x,
uint256 y,
uint256 denominator,
Rounding rounding
) internalpurereturns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (rounding == Rounding.Up &&mulmod(x, y, denominator) >0) {
result +=1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/functionsqrt(uint256 a) internalpurereturns (uint256) {
if (a ==0) {
return0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.//// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.//// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`//// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.uint256 result =1<< (log2(a) >>1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision// into the expected uint128 result.unchecked {
result = (result + a / result) >>1;
result = (result + a / result) >>1;
result = (result + a / result) >>1;
result = (result + a / result) >>1;
result = (result + a / result) >>1;
result = (result + a / result) >>1;
result = (result + a / result) >>1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/functionsqrt(uint256 a, Rounding rounding) internalpurereturns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2, rounded down, of a positive value.
* Returns 0 if given 0.
*/functionlog2(uint256 value) internalpurereturns (uint256) {
uint256 result =0;
unchecked {
if (value >>128>0) {
value >>=128;
result +=128;
}
if (value >>64>0) {
value >>=64;
result +=64;
}
if (value >>32>0) {
value >>=32;
result +=32;
}
if (value >>16>0) {
value >>=16;
result +=16;
}
if (value >>8>0) {
value >>=8;
result +=8;
}
if (value >>4>0) {
value >>=4;
result +=4;
}
if (value >>2>0) {
value >>=2;
result +=2;
}
if (value >>1>0) {
result +=1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/functionlog2(uint256 value, Rounding rounding) internalpurereturns (uint256) {
unchecked {
uint256 result =log2(value);
return result + (rounding == Rounding.Up &&1<< result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10, rounded down, of a positive value.
* Returns 0 if given 0.
*/functionlog10(uint256 value) internalpurereturns (uint256) {
uint256 result =0;
unchecked {
if (value >=10**64) {
value /=10**64;
result +=64;
}
if (value >=10**32) {
value /=10**32;
result +=32;
}
if (value >=10**16) {
value /=10**16;
result +=16;
}
if (value >=10**8) {
value /=10**8;
result +=8;
}
if (value >=10**4) {
value /=10**4;
result +=4;
}
if (value >=10**2) {
value /=10**2;
result +=2;
}
if (value >=10**1) {
result +=1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/functionlog10(uint256 value, Rounding rounding) internalpurereturns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (rounding == Rounding.Up &&10**result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256, rounded down, of a positive value.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/functionlog256(uint256 value) internalpurereturns (uint256) {
uint256 result =0;
unchecked {
if (value >>128>0) {
value >>=128;
result +=16;
}
if (value >>64>0) {
value >>=64;
result +=8;
}
if (value >>32>0) {
value >>=32;
result +=4;
}
if (value >>16>0) {
value >>=16;
result +=2;
}
if (value >>8>0) {
result +=1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/functionlog256(uint256 value, Rounding rounding) internalpurereturns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (rounding == Rounding.Up &&1<< (result *8) < value ? 1 : 0);
}
}
}
Contract Source Code
File 24 of 31: PriceConverter.sol
// SPDX-License-Identifier: AGPL-3.0-onlypragmasolidity ^0.8.19;import {FixedPointMathLib} from"solmate/utils/FixedPointMathLib.sol";
import {AccessControl} from"openzeppelin-contracts/access/AccessControl.sol";
import {ZeroAddress, CallerNotAdmin} from"../errors/scErrors.sol";
import {ConstantsasC} from"../lib/Constants.sol";
import {AggregatorV3Interface} from"../interfaces/chainlink/AggregatorV3Interface.sol";
import {IwstETH} from"../interfaces/lido/IwstETH.sol";
/**
* @title Price Converter
* @notice Contract for price conversion between assets used by staking vaults.
*/contractPriceConverterisAccessControl{
usingFixedPointMathLibforuint256;
IwstETH constant wstETH = IwstETH(C.WSTETH);
eventUsdcToEthPriceFeedUpdated(addressindexed admin, address newPriceFeed);
eventStEthToEthPriceFeedUpdated(addressindexed admin, address newPriceFeed);
// Chainlink price feed (USDC -> ETH)
AggregatorV3Interface public usdcToEthPriceFeed = AggregatorV3Interface(C.CHAINLINK_USDC_ETH_PRICE_FEED);
// Chainlink price feed (stETH -> ETH)
AggregatorV3Interface public stEThToEthPriceFeed = AggregatorV3Interface(C.CHAINLINK_STETH_ETH_PRICE_FEED);
constructor(address _admin) {
_zeroAddressCheck(_admin);
_grantRole(DEFAULT_ADMIN_ROLE, _admin);
}
function_onlyAdmin() internalview{
if (!hasRole(DEFAULT_ADMIN_ROLE, msg.sender)) revert CallerNotAdmin();
}
/**
* @notice Set the chainlink price feed for USDC -> WETH.
* @param _newPriceFeed The new price feed.
*/functionsetUsdcToEthPriceFeed(address _newPriceFeed) external{
_onlyAdmin();
_zeroAddressCheck(_newPriceFeed);
usdcToEthPriceFeed = AggregatorV3Interface(_newPriceFeed);
emit UsdcToEthPriceFeedUpdated(msg.sender, address(_newPriceFeed));
}
/// @notice Set the chainlink price feed for stETH -> ETH./// @param _newPriceFeed The new price feed.functionsetStEThToEthPriceFeed(address _newPriceFeed) external{
_onlyAdmin();
_zeroAddressCheck(_newPriceFeed);
stEThToEthPriceFeed = AggregatorV3Interface(_newPriceFeed);
emit StEthToEthPriceFeedUpdated(msg.sender, address(_newPriceFeed));
}
/**
* @notice Returns the USDC fair value for the ETH amount provided.
* @param _ethAmount The amount of ETH.
*/functionethToUsdc(uint256 _ethAmount) publicviewreturns (uint256) {
(, int256 usdcPriceInEth,,,) = usdcToEthPriceFeed.latestRoundData();
return _ethAmount.divWadDown(uint256(usdcPriceInEth) * C.WETH_USDC_DECIMALS_DIFF);
}
/**
* @notice Returns the ETH fair value for the USDC amount provided.
* @param _usdcAmount The amount of USDC.
*/functionusdcToEth(uint256 _usdcAmount) publicviewreturns (uint256) {
(, int256 usdcPriceInEth,,,) = usdcToEthPriceFeed.latestRoundData();
return (_usdcAmount * C.WETH_USDC_DECIMALS_DIFF).mulWadDown(uint256(usdcPriceInEth));
}
functionethToWstEth(uint256 ethAmount) publicviewreturns (uint256) {
(, int256 price,,,) = stEThToEthPriceFeed.latestRoundData();
uint256 stEthAmount = ethAmount.divWadDown(uint256(price));
return wstETH.getWstETHByStETH(stEthAmount);
}
functionstEthToEth(uint256 _stEthAmount) publicviewreturns (uint256) {
(, int256 price,,,) = stEThToEthPriceFeed.latestRoundData();
return _stEthAmount.mulWadDown(uint256(price));
}
functionwstEthToEth(uint256 wstEthAmount) publicviewreturns (uint256) {
// wstETh to stEth using exchangeRateuint256 stEthAmount = wstETH.getStETHByWstETH(wstEthAmount);
return stEthToEth(stEthAmount);
}
function_zeroAddressCheck(address _address) internalpure{
if (_address ==address(0)) revert ZeroAddress();
}
}
Contract Source Code
File 25 of 31: SafeTransferLib.sol
// SPDX-License-Identifier: AGPL-3.0-onlypragmasolidity >=0.8.0;import {ERC20} from"../tokens/ERC20.sol";
/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values./// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)/// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer./// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller.librarySafeTransferLib{
/*//////////////////////////////////////////////////////////////
ETH OPERATIONS
//////////////////////////////////////////////////////////////*/functionsafeTransferETH(address to, uint256 amount) internal{
bool success;
/// @solidity memory-safe-assemblyassembly {
// Transfer the ETH and store if it succeeded or not.
success :=call(gas(), to, amount, 0, 0, 0, 0)
}
require(success, "ETH_TRANSFER_FAILED");
}
/*//////////////////////////////////////////////////////////////
ERC20 OPERATIONS
//////////////////////////////////////////////////////////////*/functionsafeTransferFrom(
ERC20 token,
addressfrom,
address to,
uint256 amount
) internal{
bool success;
/// @solidity memory-safe-assemblyassembly {
// Get a pointer to some free memory.let freeMemoryPointer :=mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), from) // Append the "from" argument.mstore(add(freeMemoryPointer, 36), to) // Append the "to" argument.mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument.
success :=and(
// Set success to whether the call reverted, if not we check it either// returned exactly 1 (can't just be non-zero data), or had no return data.or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
// We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.// Counterintuitively, this call must be positioned second to the or() call in the// surrounding and() call or else returndatasize() will be zero during the computation.call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)
)
}
require(success, "TRANSFER_FROM_FAILED");
}
functionsafeTransfer(
ERC20 token,
address to,
uint256 amount
) internal{
bool success;
/// @solidity memory-safe-assemblyassembly {
// Get a pointer to some free memory.let freeMemoryPointer :=mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), to) // Append the "to" argument.mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument.
success :=and(
// Set success to whether the call reverted, if not we check it either// returned exactly 1 (can't just be non-zero data), or had no return data.or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
// We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.// Counterintuitively, this call must be positioned second to the or() call in the// surrounding and() call or else returndatasize() will be zero during the computation.call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
)
}
require(success, "TRANSFER_FAILED");
}
functionsafeApprove(
ERC20 token,
address to,
uint256 amount
) internal{
bool success;
/// @solidity memory-safe-assemblyassembly {
// Get a pointer to some free memory.let freeMemoryPointer :=mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), to) // Append the "to" argument.mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument.
success :=and(
// Set success to whether the call reverted, if not we check it either// returned exactly 1 (can't just be non-zero data), or had no return data.or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
// We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.// Counterintuitively, this call must be positioned second to the or() call in the// surrounding and() call or else returndatasize() will be zero during the computation.call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
)
}
require(success, "APPROVE_FAILED");
}
}
Contract Source Code
File 26 of 31: Strings.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v4.8.0) (utils/Strings.sol)pragmasolidity ^0.8.0;import"./math/Math.sol";
/**
* @dev String operations.
*/libraryStrings{
bytes16privateconstant _SYMBOLS ="0123456789abcdef";
uint8privateconstant _ADDRESS_LENGTH =20;
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/functiontoString(uint256 value) internalpurereturns (stringmemory) {
unchecked {
uint256 length = Math.log10(value) +1;
stringmemory buffer =newstring(length);
uint256 ptr;
/// @solidity memory-safe-assemblyassembly {
ptr :=add(buffer, add(32, length))
}
while (true) {
ptr--;
/// @solidity memory-safe-assemblyassembly {
mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
}
value /=10;
if (value ==0) break;
}
return buffer;
}
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/functiontoHexString(uint256 value) internalpurereturns (stringmemory) {
unchecked {
return toHexString(value, Math.log256(value) +1);
}
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/functiontoHexString(uint256 value, uint256 length) internalpurereturns (stringmemory) {
bytesmemory buffer =newbytes(2* length +2);
buffer[0] ="0";
buffer[1] ="x";
for (uint256 i =2* length +1; i >1; --i) {
buffer[i] = _SYMBOLS[value &0xf];
value >>=4;
}
require(value ==0, "Strings: hex length insufficient");
returnstring(buffer);
}
/**
* @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
*/functiontoHexString(address addr) internalpurereturns (stringmemory) {
return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
}
}
Contract Source Code
File 27 of 31: Swapper.sol
// SPDX-License-Identifier: AGPL-3.0-onlypragmasolidity ^0.8.19;import {ERC20} from"solmate/tokens/ERC20.sol";
import {WETH} from"solmate/tokens/WETH.sol";
import {ILido} from"../interfaces/lido/ILido.sol";
import {IwstETH} from"../interfaces/lido/IwstETH.sol";
import {ICurvePool} from"../interfaces/curve/ICurvePool.sol";
import {SafeTransferLib} from"solmate/utils/SafeTransferLib.sol";
import {Address} from"openzeppelin-contracts/utils/Address.sol";
import {AmountReceivedBelowMin} from"../errors/scErrors.sol";
import {ISwapRouter} from"../interfaces/uniswap/ISwapRouter.sol";
import {ConstantsasC} from"../lib/Constants.sol";
/**
* @title Swapper
* @notice Contract facilitating token swaps on Uniswap V3 and 0x.
* @dev This contract is only meant to be used via delegatecalls from another contract.
* @dev Using this contract directly for swaps might result in reverts.
*/contractSwapper{
usingSafeTransferLibforERC20;
usingAddressforaddress;
// Uniswap V3 router
ISwapRouter publicconstant swapRouter = ISwapRouter(C.UNISWAP_V3_SWAP_ROUTER);
ICurvePool publicconstant curvePool = ICurvePool(C.CURVE_ETH_STETH_POOL);
WETH publicconstant weth = WETH(payable(C.WETH));
ILido publicconstant stEth = ILido(C.STETH);
IwstETH publicconstant wstETH = IwstETH(C.WSTETH);
/**
* @notice Swap tokens on Uniswap V3 using exact input single function.
* @param _tokenIn Address of the token to swap.
* @param _tokenOut Address of the token to receive.
* @param _amountIn Amount of the token to swap.
* @param _amountOutMin Minimum amount of the token to receive.
*/functionuniswapSwapExactInput(
ERC20 _tokenIn,
ERC20 _tokenOut,
uint256 _amountIn,
uint256 _amountOutMin,
uint24 _poolFee
) externalreturns (uint256) {
ERC20(_tokenIn).safeApprove(address(swapRouter), _amountIn);
ISwapRouter.ExactInputSingleParams memory params = ISwapRouter.ExactInputSingleParams({
tokenIn: address(_tokenIn),
tokenOut: address(_tokenOut),
fee: _poolFee,
recipient: address(this),
deadline: block.timestamp,
amountIn: _amountIn,
amountOutMinimum: _amountOutMin,
sqrtPriceLimitX96: 0
});
return swapRouter.exactInputSingle(params);
}
/**
* @notice Swap tokens on Uniswap V3 using exact output single function.
* @param _tokenIn Address of the token to swap.
* @param _tokenOut Address of the token to receive.
* @param _amountOut Amount of the token to receive.
* @param _amountInMaximum Maximum amount of the token to swap.
*/functionuniswapSwapExactOutput(
ERC20 _tokenIn,
ERC20 _tokenOut,
uint256 _amountOut,
uint256 _amountInMaximum,
uint24 _poolFee
) externalreturns (uint256) {
ISwapRouter.ExactOutputSingleParams memory params = ISwapRouter.ExactOutputSingleParams({
tokenIn: address(_tokenIn),
tokenOut: address(_tokenOut),
fee: _poolFee,
recipient: address(this),
deadline: block.timestamp,
amountOut: _amountOut,
amountInMaximum: _amountInMaximum,
sqrtPriceLimitX96: 0
});
_tokenIn.safeApprove(address(swapRouter), _amountInMaximum);
uint256 amountIn = swapRouter.exactOutputSingle(params);
_tokenIn.safeApprove(address(swapRouter), 0);
return amountIn;
}
/**
* @notice Swap tokens on 0x protocol.
* @param _tokenIn Address of the token to swap.
* @param _tokenOut Address of the token to receive.
* @param _amountIn Amount of the token to swap.
* @param _amountOutMin Minimum amount of the token to receive.
* @param _swapData Encoded swap data obtained from 0x API.
*/functionzeroExSwap(
ERC20 _tokenIn,
ERC20 _tokenOut,
uint256 _amountIn,
uint256 _amountOutMin,
bytescalldata _swapData
) externalreturns (uint256) {
uint256 tokenOutInitialBalance = _tokenOut.balanceOf(address(this));
_tokenIn.safeApprove(C.ZERO_EX_ROUTER, _amountIn);
C.ZERO_EX_ROUTER.functionCall(_swapData);
uint256 amountReceived = _tokenOut.balanceOf(address(this)) - tokenOutInitialBalance;
if (amountReceived < _amountOutMin) revert AmountReceivedBelowMin();
_tokenIn.approve(C.ZERO_EX_ROUTER, 0);
return amountReceived;
}
functionlidoSwapWethToWstEth(uint256 _wethAmount) external{
// weth to eth
weth.withdraw(_wethAmount);
// stake to lido / eth => stETH
stEth.submit{value: _wethAmount}(address(0x00));
// stETH to wstEthuint256 stEthBalance = stEth.balanceOf(address(this));
ERC20(address(stEth)).safeApprove(address(wstETH), stEthBalance);
wstETH.wrap(stEthBalance);
}
functioncurveSwapStEthToWeth(uint256 _stEthAmount, uint256 _wethAmountOutMin)
externalreturns (uint256 wethReceived)
{
// stETH to eth
ERC20(address(stEth)).safeApprove(address(curvePool), _stEthAmount);
wethReceived = curvePool.exchange(1, 0, _stEthAmount, _wethAmountOutMin);
// eth to weth
weth.deposit{value: address(this).balance}();
}
}
// SPDX-License-Identifier: AGPL-3.0-onlypragmasolidity ^0.8.13;import {
ZeroAddress,
InvalidSlippageTolerance,
InsufficientDepositBalance,
FloatBalanceTooLow
} from"../errors/scErrors.sol";
import {ERC20} from"solmate/tokens/ERC20.sol";
import {WETH} from"solmate/tokens/WETH.sol";
import {SafeTransferLib} from"solmate/utils/SafeTransferLib.sol";
import {FixedPointMathLib} from"solmate/utils/FixedPointMathLib.sol";
import {Address} from"openzeppelin-contracts/utils/Address.sol";
import {EnumerableMap} from"openzeppelin-contracts/utils/structs/EnumerableMap.sol";
import {ConstantsasC} from"../lib/Constants.sol";
import {BaseV2Vault} from"./BaseV2Vault.sol";
import {IAdapter} from"./IAdapter.sol";
import {IwstETH} from"../interfaces/lido/IwstETH.sol";
import {PriceConverter} from"./PriceConverter.sol";
import {Swapper} from"./Swapper.sol";
/**
* @title Sandclock WETH Vault version 2
* @notice Deposit Asset : Weth or Eth
* This vault leverages the supplied weth using flashloans, stakes the leveraged eth, supplies the wstEth as collateral
* and subesequently borrows weth on that collateral to payback the flashloan
* The bulk of the interest is earned from staking eth
* In contrast to scWETHv1 which used only one pre coded lending market
* scWETHv2 can use multiple lending markets, which can be controlled by adding or removing adapter contracts into the vault
*/contractscWETHv2isBaseV2Vault{
usingSafeTransferLibforERC20;
usingFixedPointMathLibforuint256;
usingAddressforaddress;
usingEnumerableMapforEnumerableMap.UintToAddressMap;
eventHarvested(uint256 profitSinceLastHarvest, uint256 performanceFee);
eventMinFloatAmountUpdated(addressindexed user, uint256 newMinFloatAmount);
eventRebalanced(uint256 totalCollateral, uint256 totalDebt, uint256 floatBalance);
eventSuppliedAndBorrowed(uint256 adapterId, uint256 supplyAmount, uint256 borrowAmount);
eventRepaidAndWithdrawn(uint256 adapterId, uint256 repayAmount, uint256 withdrawAmount);
eventWithdrawnToVault(uint256 amount);
// total invested during last harvest/rebalanceuint256public totalInvested;
// total profit generated for this vaultuint256public totalProfit;
// since the totalAssets increases after profit, the floatRequired also increases proportionally in case of using a percentage float// this will cause the receiveFlashloan method to fail on reinvesting profits (using rebalance) after the multicall, since the actual float in the contract remain unchanged after the multicall// this can be fixed by also withdrawing float into the contract in the reinvesting profits multicall but that makes the calculations very complex on the backend// a simple solution to that is just using minimumFloatAmount instead of a percentage floatuint256public minimumFloatAmount =1ether;
IwstETH constant wstETH = IwstETH(C.WSTETH);
constructor(address _admin, address _keeper, WETH _weth, Swapper _swapper, PriceConverter _priceConverter)
BaseV2Vault(_admin, _keeper, _weth, _priceConverter, _swapper, "Sandclock WETH Vault v2", "scWETHv2")
{
zeroExSwapWhitelist[ERC20(C.WSTETH)] =true;
}
/*//////////////////////////////////////////////////////////////
PUBLIC API
//////////////////////////////////////////////////////////////*/// need to be able to receive ethreceive() externalpayable{}
/// @notice set the minimum amount of weth that must be present in the vault/// @param _newMinFloatAmount the new minimum float amountfunctionsetMinimumFloatAmount(uint256 _newMinFloatAmount) external{
_onlyAdmin();
minimumFloatAmount = _newMinFloatAmount;
emit MinFloatAmountUpdated(msg.sender, _newMinFloatAmount);
}
/// @notice the primary method to be used by backend to invest, disinvest or reallocate funds among supported adapters/// @dev _totalInvestAmount must be zero in case of disinvest, reallocation or reinvesting profits/// @dev also mints performance fee tokens to the treasury based on the profits (if any) made by the vault/// @param _totalInvestAmount total amount of float in the strategy to invest in the lending markets in case of a invest/// @param _flashLoanAmount the amount to be flashloaned from balancer/// @param _multicallData array of bytes containing the series of encoded functions to be called (the functions being one of supplyAndBorrow, repayAndWithdraw, swapWstEthToWeth, swapWethToWstEth, zeroExSwap)functionrebalance(uint256 _totalInvestAmount, uint256 _flashLoanAmount, bytes[] calldata _multicallData)
external{
_onlyKeeper();
if (_totalInvestAmount > asset.balanceOf(address(this))) revert InsufficientDepositBalance();
// needed otherwise counted as profit during harvest
totalInvested += _totalInvestAmount;
_flashLoan(_flashLoanAmount, _multicallData);
_harvest();
emit Rebalanced(totalCollateral(), totalDebt(), asset.balanceOf(address(this)));
}
/// @notice swap weth to wstEth/// @dev the keeper will mostly use 0x (zeroExSwap method) for swapping weth to wstEth between rebalancing/// @dev this method is just a precaution and to be only used by the keeper in case zeroEx API goes down/// @param _wethAmount amount of weth to be swapped to wstEthfunctionswapWethToWstEth(uint256 _wethAmount) external{
_onlyKeeperOrFlashLoan();
address(swapper).functionDelegateCall(
abi.encodeWithSelector(Swapper.lidoSwapWethToWstEth.selector, _wethAmount)
);
}
/// @notice swap wstEth to weth/// @dev mainly to be used in the multicall to swap withdrawn wstEth to weth to payback the flashloan/// @param _wstEthAmount amount of wstEth to be swapped to weth/// @param _slippageTolerance the max slippage during steth to eth swap (1e18 meaning 0 slippage tolerance)functionswapWstEthToWeth(uint256 _wstEthAmount, uint256 _slippageTolerance) external{
_onlyKeeperOrFlashLoan();
if (_slippageTolerance > C.ONE) revert InvalidSlippageTolerance();
uint256 wstEthBalance = wstETH.balanceOf(address(this));
if (_wstEthAmount > wstEthBalance) {
_wstEthAmount = wstEthBalance;
}
uint256 stEthAmount = wstETH.unwrap(_wstEthAmount);
uint256 wethAmountOutMin = priceConverter.stEthToEth(stEthAmount).mulWadDown(_slippageTolerance);
address(swapper).functionDelegateCall(
abi.encodeWithSelector(Swapper.curveSwapStEthToWeth.selector, stEthAmount, wethAmountOutMin)
);
}
/// @notice withdraw deposited funds from the lending markets to the vault/// @param _amount : amount of assets to withdraw to the vaultfunctionwithdrawToVault(uint256 _amount) external{
_onlyKeeper();
_withdrawToVault(_amount);
}
/// @notice returns the adapter address given the adapterId (only if the adaapterId is supported else returns zero address)/// @param _adapterId the id of the adapter to checkfunctiongetAdapter(uint256 _adapterId) externalviewreturns (address adapter) {
(, adapter) = protocolAdapters.tryGet(_adapterId);
}
/// @notice returns the total assets (in WETH) held by the strategyfunctiontotalAssets() publicviewoverridereturns (uint256 assets) {
// value of the supplied collateral in eth terms using chainlink oracle
assets = _totalCollateralInWeth();
// subtract the debt
assets -= totalDebt();
// add float
assets += asset.balanceOf(address(this));
}
/// @notice returns the wstEth deposited of the vault in a particular protocol/// @param _adapterId The id the protocol adapterfunctiongetCollateral(uint256 _adapterId) publicviewreturns (uint256) {
if (!isSupported(_adapterId)) return0;
return IAdapter(protocolAdapters.get(_adapterId)).getCollateral(address(this));
}
/// @notice returns the total wstEth supplied as collateralfunctiontotalCollateral() publicviewreturns (uint256 collateral) {
uint256 n = protocolAdapters.length();
address adapter;
for (uint256 i; i < n; i++) {
(, adapter) = protocolAdapters.at(i);
collateral += IAdapter(adapter).getCollateral(address(this));
}
}
/// @notice returns the weth debt of the vault in a particularly protocol/// @param _adapterId The id the protocol adapterfunctiongetDebt(uint256 _adapterId) publicviewreturns (uint256) {
if (!isSupported(_adapterId)) return0;
return IAdapter(protocolAdapters.get(_adapterId)).getDebt(address(this));
}
/// @notice returns the total WETH borrowedfunctiontotalDebt() publicviewreturns (uint256 debt) {
uint256 n = protocolAdapters.length();
address adapter;
for (uint256 i; i < n; i++) {
(, adapter) = protocolAdapters.at(i);
debt += IAdapter(adapter).getDebt(address(this));
}
}
/// @notice helper method for the user to directly deposit ETH to this vault instead of weth/// @param receiver the address to mint the shares tofunctiondeposit(address receiver) externalpayablereturns (uint256 shares) {
uint256 assets =msg.value;
// Check for rounding error since we round down in previewDeposit.require((shares = previewDeposit(assets)) !=0, "ZERO_SHARES");
// wrap eth
WETH(payable(address(asset))).deposit{value: assets}();
_mint(receiver, shares);
emit Deposit(msg.sender, receiver, assets, shares);
afterDeposit(assets, shares);
}
/// @dev called after the flashLoan on rebalancefunctionreceiveFlashLoan(address[] memory,
uint256[] memory amounts,
uint256[] memory feeAmounts,
bytesmemory userData
) external{
_isFlashLoanInitiated();
// decode user databytes[] memory callData =abi.decode(userData, (bytes[]));
_multiCall(callData);
// payback flashloan
asset.safeTransfer(address(balancerVault), amounts[0] + feeAmounts[0]);
_enforceFloat();
}
/// @notice supplies wstEth as collateral and borrows weth from the respective protocol as specified by adapterId/// @dev mainly to be used inside the multicall to supply and borrow assets from the respective lending market/// @param _adapterId the id of the adapter for the required protocol/// @param _supplyAmount the amount of wstEth to be supplied as collateral/// @param _borrowAmount the amount of weth to be borrowedfunctionsupplyAndBorrow(uint256 _adapterId, uint256 _supplyAmount, uint256 _borrowAmount) external{
_onlyKeeperOrFlashLoan();
address adapter = protocolAdapters.get(_adapterId);
_adapterDelegateCall(adapter, abi.encodeWithSelector(IAdapter.supply.selector, _supplyAmount));
_adapterDelegateCall(adapter, abi.encodeWithSelector(IAdapter.borrow.selector, _borrowAmount));
emit SuppliedAndBorrowed(_adapterId, _supplyAmount, _borrowAmount);
}
/// @notice repays weth debt and withdraws wstEth collateral from the respective protocol as specified by adapterId/// @dev mainly to be used inside the multicall to repay and withdraw assets from the respective lending market/// @param _adapterId the id of the adapter for the required protocol/// @param _repayAmount the amount of weth to be repaid/// @param _withdrawAmount the amount of wstEth to be withdrawnfunctionrepayAndWithdraw(uint256 _adapterId, uint256 _repayAmount, uint256 _withdrawAmount) external{
_onlyKeeperOrFlashLoan();
address adapter = protocolAdapters.get(_adapterId);
_adapterDelegateCall(adapter, abi.encodeWithSelector(IAdapter.repay.selector, _repayAmount));
_adapterDelegateCall(adapter, abi.encodeWithSelector(IAdapter.withdraw.selector, _withdrawAmount));
emit RepaidAndWithdrawn(_adapterId, _repayAmount, _withdrawAmount);
}
functionwithdraw(uint256 assets, address receiver, address owner) publicoverridereturns (uint256 shares) {
shares = previewWithdraw(assets); // No need to check for rounding error, previewWithdraw rounds up.if (msg.sender!= owner) {
uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals.if (allowed !=type(uint256).max) allowance[owner][msg.sender] = allowed - shares;
}
beforeWithdraw(assets, shares);
_burn(owner, shares);
uint256 balance = asset.balanceOf(address(this));
// since during withdrawing everything,// actual withdrawn amount might be less than totalAsssets// (due to slippage incurred during wstEth to weth swap)if (assets > balance) {
assets = balance;
}
emit Withdraw(msg.sender, receiver, owner, assets, shares);
asset.safeTransfer(receiver, assets);
}
functionredeem(uint256 shares, address receiver, address owner) publicoverridereturns (uint256 assets) {
if (msg.sender!= owner) {
uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals.if (allowed !=type(uint256).max) allowance[owner][msg.sender] = allowed - shares;
}
// Check for rounding error since we round down in previewRedeem.require((assets = previewRedeem(shares)) !=0, "ZERO_ASSETS");
beforeWithdraw(assets, shares);
_burn(owner, shares);
uint256 balance = asset.balanceOf(address(this));
// since during withdrawing everything,// actual withdrawn amount might be less than totalAsssets// (due to slippage incurred during wstEth to weth swap)if (assets > balance) {
assets = balance;
}
emit Withdraw(msg.sender, receiver, owner, assets, shares);
asset.safeTransfer(receiver, assets);
}
/*//////////////////////////////////////////////////////////////
INTERNAL API
//////////////////////////////////////////////////////////////*/function_withdrawToVault(uint256 _amount) internal{
uint256 n = protocolAdapters.length();
uint256 flashLoanAmount;
uint256 totalInvested_ = _totalCollateralInWeth() - totalDebt();
bytes[] memory callData =newbytes[](n +1); // +1 for the last call to swap wstEth to weth// limit the amount to withdraw to the total invested amountif (_amount > totalInvested_) _amount = totalInvested_;
uint256 id;
address adapter;
uint256 repayPerProtocol;
uint256 withdrawPerProtocol;
for (uint256 i; i < n; i++) {
(id, adapter) = protocolAdapters.at(i);
uint256 collateral = IAdapter(adapter).getCollateral(address(this));
// skip if there is no position on this protocolif (collateral ==0) continue;
uint256 debt = IAdapter(adapter).getDebt(address(this));
uint256 assets = priceConverter.wstEthToEth(collateral) - debt;
// withdraw from each protocol in equal weight (based on the relative allocation)
withdrawPerProtocol = _amount.mulDivDown(assets, totalInvested_);
repayPerProtocol = withdrawPerProtocol.mulDivDown(debt, assets);
flashLoanAmount += repayPerProtocol;
callData[i] =abi.encodeWithSelector(
this.repayAndWithdraw.selector,
id,
repayPerProtocol,
priceConverter.ethToWstEth(repayPerProtocol + withdrawPerProtocol)
);
}
// needed otherwise counted as loss during harvest
totalInvested -= _amount;
callData[n] =abi.encodeWithSelector(scWETHv2.swapWstEthToWeth.selector, type(uint256).max, slippageTolerance);
uint256 float = asset.balanceOf(address(this));
_flashLoan(flashLoanAmount, callData);
emit WithdrawnToVault(asset.balanceOf(address(this)) - float);
}
/// @notice reverts if float in the vault is not above the minimum requiredfunction_enforceFloat() internalview{
uint256 float = asset.balanceOf(address(this));
uint256 floatRequired = minimumFloatAmount;
if (float < floatRequired) revert FloatBalanceTooLow(float, floatRequired);
}
functionbeforeWithdraw(uint256 assets, uint256) internaloverride{
uint256 float = asset.balanceOf(address(this));
if (assets <= float) return;
uint256 missing = assets + minimumFloatAmount - float;
_withdrawToVault(missing);
}
function_flashLoan(uint256 _totalFlashLoanAmount, bytes[] memory callData) internal{
address[] memory tokens =newaddress[](1);
tokens[0] =address(asset);
uint256[] memory amounts =newuint256[](1);
amounts[0] = _totalFlashLoanAmount;
_initiateFlashLoan();
balancerVault.flashLoan(address(this), tokens, amounts, abi.encode(callData));
_finalizeFlashLoan();
}
function_harvest() internal{
// store the old totaluint256 oldTotalInvested = totalInvested;
uint256 assets = _totalCollateralInWeth() - totalDebt();
if (assets > oldTotalInvested) {
totalInvested = assets;
// profit since last harvest, zero if there was a lossuint256 profit = assets - oldTotalInvested;
totalProfit += profit;
uint256 fee = profit.mulWadDown(performanceFee);
// mint equivalent amount of tokens to the performance fee beneficiary ie the treasury
_mint(treasury, convertToShares(fee));
emit Harvested(profit, fee);
}
}
function_totalCollateralInWeth() internalviewreturns (uint256) {
return priceConverter.wstEthToEth(totalCollateral());
}
}