Contract Name:
WrappedTokenGateway
Contract Source Code:
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.19;
import {IWrappedToken} from "./interfaces/IWrappedToken.sol";
import {IMaxApyVault} from "./interfaces/IMaxApyVault.sol";
import {SafeTransferLib} from "solady/utils/SafeTransferLib.sol";
/*KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK
KKKKK0OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO0KKKKKKK
KK0dcclllllllllllllllllllllllllllllccccccccccccccccccclx0KKK
KOc,dKNWWWWWWWWWWWWWWWWWWWWWWWWWWWWNNNNNNNNNNNNNNNNNXOl';xKK
Kd'oWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMX; ,kK
Ko'xMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMNc .dK
Ko'dMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMNc .oK
Kd'oWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMNc .oK
KO:,xXWWWWWWWWWWWWWWWWWWWWMMMMMMMMMMMMMMMMMMMMMMMMMMMMNc .oK
KKOl,',;;,,,,,,;;,,,,,,,;;cxXMMMMMMMMMMMMMMMMMMMMMMMMMNc .oK
KKKKOoc;;;;;;;;;;;;;;;;;;;,.cXMMMMMMMMMMMMMMMMMMMMMMMMNc .oK
KKKKKKKKKKKKKKKKKKKK00O00K0:,0MMMMMMMMMMMMMMMMMMMMMMMMNc .oK
KKKKKKKKKKKKKKKKKKklcccccld;,0MMMMMMMMMMMMMMMMMMMMMMMMNc .oK
KKKKKKKKKKKKKKKKkl;ckXNXOc. '0MMMMMMMMMMMMMMMMMMMMMMMMNc .oK
KKKKKKKKKKKKKKkc;l0WMMMMMX; .oKNMMMMMMMMMMMMMMMMMMMMMMNc .oK
KKKKKKKKKKKKkc;l0WMMMMMMMWd. .,lddddddxONMMMMMMMMMMMMNc .oK
KKKKKKKKKKkc;l0WMMMMMMMMMMWOl::;'. .....:0WMMMMMMMMMMNc .oK
KKKKKKK0xc;o0WMMMMMMMMMMMMMMMMMWNk'.;xkko'lNMMMMMMMMMMNc .oK
KKKKK0x:;oKWMMMMMMMMMMMMMMMMMMMMMWd..lKKk,lNMMMMMMMMMMNc .oK
KKK0x:;oKWMMMMMMMMMMMMMMMMMMMMMMWO, c0Kk,lNMMMMMMMMMMNc .oK
KKx:;dKWMMMMMMMMMMMMMMMMMMMMMWN0c. ;kKKk,lNMMMMMMMMMMNc .oK
Kx,:KWMMMMMMMMMMMMMMMMMMMMMW0c,. 'oOKKKk,lNMMMMMMMMMMNc .oK
Ko'xMMMMMMMMMMMMMMMMMMMMMW0c. 'oOKKKKKk,lNMMMMMMMMMMNc .oK
Ko'xMMMMMMMMMMMMMMMMMMMW0c. ':oOKKKKKKKk,lNMMMMMMMMMMNc .oK
Ko'xMMMMMMMMMMMMMMMMMW0l. 'oOKKKKKKKKKKk,cNMMMMMMMMMMNc .oK
Ko'xMMMMMMMMMMMMMMMW0l. 'oOKKKKKKKKKKKKk,lNMMMMMMMMMMNc .oK
Ko'dWMMMMMMMMMMMMW0l. 'oOKKKKKKKKKKKKKKk,cNMMMMMMMMMMX: .oK
KO:,xXNWWWWWWWWNOl. 'oOKKKKKKKKKKKKKKKK0c,xNMMMMMMMMNd. .dK
KKOl''',,,,,,,,.. 'oOKKKKKKKKKKKKKKKKKKKOl,,ccccccc:' .c0K
KKKKOoc:;;;;;;;;:ldOKKKKKKKKKKKKKKKKKKKKKKKkl;'......',cx0KK
KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK0OOOOOOO0KKK*/
/// @notice Helper contract to interact with a MaxApy Vault utilizing the chain's native token the protocol
/// has been deployed to
/// @author MaxApy
contract WrappedTokenGateway {
using SafeTransferLib for address;
////////////////////////////////////////////////////////////////
/// CONSTANTS ///
////////////////////////////////////////////////////////////////
/// @notice The chain's wrapped native token
IWrappedToken public immutable wrappedToken;
/// @notice The MaxApy vault linked to this Gateway contract
IMaxApyVault public immutable vault;
////////////////////////////////////////////////////////////////
/// ERRORS ///
////////////////////////////////////////////////////////////////
error InvalidZeroValue();
error FailedNativeTransfer();
error ReceiveNotAllowed();
////////////////////////////////////////////////////////////////
/// EVENTS ///
////////////////////////////////////////////////////////////////
/// @notice Emitted on native vault deposits
event DepositNative(address indexed recipient, uint256 shares, uint256 amount);
/// @notice Emitted on native vault withdrawals
event WithdrawNative(address indexed recipient, uint256 shares, uint256 amount);
/// @dev `keccak256(bytes("DepositNative(address,uint256,uint256)"))`.
uint256 internal constant _DEPOSIT_NATIVE_EVENT_SIGNATURE =
0x6bb902f8baf2580ae3dae24e58f4b874ecca85152076af921bfd172dce1c7e28;
/// @dev `keccak256(bytes("WithdrawNative(address,uint256,uint256)"))`.
uint256 internal constant _WITHDRAW_NATIVE_EVENT_SIGNATURE =
0x5cb35f4e7dbc40dd34f0d58cec4f5548fc47638cb46d7964e4f07c48e97e4c7d;
////////////////////////////////////////////////////////////////
/// CONSTRUCTOR ///
////////////////////////////////////////////////////////////////
/// @notice Create the WrappedToken Gateway
/// @param _wrappedToken The wrapped token of the chain the contract will be deployed to
/// @param _vault The MaxApy vault linked to this Gateway contract
constructor(IWrappedToken _wrappedToken, IMaxApyVault _vault) {
wrappedToken = _wrappedToken;
vault = _vault;
address(_wrappedToken).safeApprove(address(_vault), type(uint256).max);
}
////////////////////////////////////////////////////////////////
/// GATEWAY CORE LOGIC ///
////////////////////////////////////////////////////////////////
/// @notice Deposits `msg.value` of `_wrappedToken`, issuing shares to `recipient`
/// @param recipient The address to issue the shares from MaxApy's Vault to
function depositNative(address recipient) external payable returns (uint256) {
// Cache `wrappedToken` and `vault` due to assembly's immutable access restrictions
address cachedWrappedToken = address(wrappedToken);
address cachedVault = address(vault);
assembly ("memory-safe") {
// Check if `msg.value` is 0
if iszero(callvalue()) {
// Throw the `InvalidZeroValue()` error
mstore(0x00, 0xef7a63d0)
revert(0x1c, 0x04)
}
// Cache the free memory pointer
let m := mload(0x40)
// Store Wrapped Token's `deposit()` function selector:
// `bytes4(keccak256("deposit()"))`
mstore(0x00, 0xd0e30db0)
// Deposit native token in exchange for wrapped native token
// Note: using some wrapped tokens' fallback function for deposit allows saving the previous
// selector loading into memory to call wrappedToken's `deposit()`.
// This is avoided due to some chain's wrapped native versions not allowing such behaviour
if iszero(
call(
gas(), // Remaining amount of gas
cachedWrappedToken, // Address of `wrappedToken`
callvalue(), // `msg.value`
0x1c, // byte offset in memory where calldata starts
0x24, // size of the calldata to copy
0x00, // byte offset in memory to store the return data
0x00 // size of the return data
)
) {
// Throw the `WrappedTokenDepositFailed()` error
mstore(0x00, 0x22cd2378)
revert(0x1c, 0x04)
}
// Store MaxApy vault's `deposit()` function selector:
// `bytes4(keccak256("deposit(uint256,address)"))`
mstore(0x00, 0x6e553f65)
mstore(0x20, callvalue()) // Append the `amount` argument
mstore(0x40, recipient) // Append the `recipient` argument
// Deposit into MaxApy vault
if iszero(
call(
gas(), // Remaining amount of gas
cachedVault, // Address of `vault`
0, // `msg.value`
0x1c, // byte offset in memory where calldata starts
0x44, // size of the calldata to copy
0x00, // byte offset in memory to store the return data
0x20 // size of the return data
)
) {
// If call failed, throw the error thrown in the previous `call`
revert(0x00, 0x04)
}
// Emit the `DepositNative` event
mstore(0x20, callvalue())
log2(0x00, 0x40, _DEPOSIT_NATIVE_EVENT_SIGNATURE, recipient)
mstore(0x40, m) // Restore the free memory pointer
return(0x00, 0x20) // Return `shares` value stored in 0x00 from previous from call's
}
}
/// @notice Withdraws the calling account's tokens from MaxApy's Vault, redeeming
/// amount `shares` for the corresponding amount of tokens, which will be transferred to
/// `recipient` in the form of the chain's native token
/// @param shares How many shares to try and redeem for tokens
/// @param recipient The address to issue the shares from MaxApy's Vault to
/// @param maxLoss The maximum acceptable loss to sustain on withdrawal. Up to loss specified amount of shares may be
/// burnt to cover losses on withdrawal
function withdrawNative(uint256 shares, address recipient, uint256 maxLoss) external returns (uint256) {
// Cache `wrappedToken` and `vault` due to assembly's immutable access restrictions
address cachedWrappedToken = address(wrappedToken);
address cachedVault = address(vault);
assembly ("memory-safe") {
// Check if `shares` passed by user is `type(uint256).max`
if eq(shares, not(0)) {
// Store `vault`'s `balanceOf()` function selector:
// `bytes4(keccak256("balanceOf(address)"))`
mstore(0x00, 0x70a08231)
mstore(0x20, caller()) // append the `owner` argument as `msg.sender`
// query `vault`'s `msg.sender` `balanceOf()`
if iszero(
staticcall(
gas(), // Remaining amount of gas
cachedVault, // Address of `vault`
0x1c, // byte offset in memory where calldata starts
0x24, // size of the calldata to copy
0x00, // byte offset in memory to store the return data
0x20 // size of the return data
)
) {
// Revert if balance query fails
revert(0x00, 0x04)
}
// Store `msg.sender`'s balance returned by staticcall into `shares`
shares := mload(0x00)
}
}
// Transfer caller shares
address(vault).safeTransferFrom(msg.sender, address(this), shares);
uint256 amountWithdrawn;
assembly ("memory-safe") {
// Cache the free memory pointer
let m := mload(0x40)
// Store `vault`'s `withdraw()` function selector:
// `bytes4(keccak256("withdraw(address)"))`
mstore(0x00, 0xe63697c8)
mstore(0x20, shares) // append the `shares` argument
mstore(0x40, address()) // append the `address(this)` argument
mstore(0x60, maxLoss) // append the `maxLoss` argument
// Withdraw from MaxApy vault
if iszero(
call(
gas(), // Remaining amount of gas
cachedVault, // Address of `vault`
0, // `msg.value`
0x1c, // byte offset in memory where calldata starts
0x64, // size of the calldata to copy
0x00, // byte offset in memory to store the return data
0x20 // size of the return data
)
) {
// If call failed, throw the error thrown in the previous `call`
revert(0x00, 0x04)
}
// Store `amountWithdrawn` returned by the previous call to `withdraw()`
amountWithdrawn := mload(0x00)
// Store `wrappedToken`'s `withdraw()` function selector:
// `bytes4(keccak256("withdraw(uint256)"))`
mstore(0x00, 0x2e1a7d4d)
mstore(0x20, amountWithdrawn) // append the `amountWithdrawn` argument
// Withdraw from wrapped token
if iszero(
call(
gas(), // Remaining amount of gas
cachedWrappedToken, // Address of `vault`
0, // `msg.value`
0x1c, // byte offset in memory where calldata starts
0x24, // size of the calldata to copy
0x00, // byte offset in memory to store the return data
0x20 // size of the return data
)
) {
// If call failed, throw the error thrown in the previous `call`
revert(0x00, 0x04)
}
// Transfer native token back to user
if iszero(call(gas(), recipient, amountWithdrawn, 0x00, 0x00, 0x00, 0x00)) {
// If call failed, throw the `FailedNativeTransfer()` error
mstore(0x00, 0x3c3f4130)
revert(0x1c, 0x04)
}
// Emit the `WithdrawNative` event
mstore(0x00, shares)
mstore(0x20, amountWithdrawn)
log2(0x00, 0x40, _WITHDRAW_NATIVE_EVENT_SIGNATURE, recipient)
mstore(0x60, 0) // Restore the zero slot
mstore(0x40, m) // Restore the free memory pointer
return(0x20, 0x20) // Return `amountWithdrawn` value stored in 0x00 from previous from call's
}
}
////////////////////////////////////////////////////////////////
/// RECEIVE() function ///
////////////////////////////////////////////////////////////////
/// @notice Receive function to accept native transfers
/// @dev Note only the chain's wrapped token will be able to perform native token transfers
/// to this contract
receive() external payable {
// Cache `wrappedToken` due to assembly immutable access restrictions
address cachedWrappedToken = address(wrappedToken);
assembly {
// Check if caller is not the `wrappedToken`
if iszero(eq(caller(), cachedWrappedToken)) {
// Throw the `ReceiveNotAllowed()` error
mstore(0x00, 0xcb263c3f)
revert(0x1c, 0x04)
}
}
}
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.19;
interface IWrappedToken {
function deposit() external payable;
function transfer(address to, uint256 value) external returns (bool);
function withdraw(uint256) external;
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.19;
import {StrategyData} from "../helpers/VaultTypes.sol";
/**
* @notice MaxApyVault contains the main interface for MaxApy Vaults
*/
interface IMaxApyVault {
/// User-facing vault functions
function deposit(uint256 amount, address recipient) external returns (uint256);
function withdraw(uint256 shares, address recipient, uint256 maxLoss) external returns (uint256);
function report(uint128 gain, uint128 loss, uint128 debtPayment) external returns (uint256);
/// ERC20 Token functions
function name() external returns (string memory);
function symbol() external returns (string memory);
function decimals() external returns (uint8);
function underlyingAsset() external view returns (address);
function totalSupply() external view returns (uint256);
function balanceOf(address user) external view returns (uint256);
/// Ownership
function transferOwnership(address newOwner) external payable;
function renounceOwnership() external payable;
function requestOwnershipHandover() external payable;
function cancelOwnershipHandover() external payable;
function completeOwnershipHandover(address pendingOwner) external payable;
/// View ownership
function ownershipHandoverExpiresAt(address pendingOwner) external view returns (uint256);
function ownershipHandoverValidFor() external view returns (uint64);
function owner() external view returns (address result);
/// Roles
function grantRoles(address user, uint256 roles) external payable;
function revokeRoles(address user, uint256 roles) external payable;
function renounceRoles(uint256 roles) external payable;
/// View roles
function ADMIN_ROLE() external returns (uint256);
function EMERGENCY_ADMIN_ROLE() external returns (uint256);
function KEEPER_ROLE() external returns (uint256);
function STRATEGY_ROLE() external returns (uint256);
function hasAnyRole(address user, uint256 roles) external view returns (bool result);
function hasAllRoles(address user, uint256 roles) external view returns (bool result);
function rolesOf(address user) external view returns (uint256 roles);
function rolesFromOrdinals(uint8[] memory ordinals) external pure returns (uint256 roles);
function ordinalsFromRoles(uint256 roles) external pure returns (uint8[] memory ordinals);
/// Vault configuration
function debtRatio() external returns (uint256);
function totalDebt() external returns (uint256);
function totalIdle() external returns (uint256);
function strategies(address strategy) external returns (StrategyData memory);
function withdrawalQueue(uint256 index) external returns (address);
function emergencyShutdown() external returns (bool);
/// Vault management
function setEmergencyShutdown(bool _emergencyShutdown) external;
function addStrategy(
address newStrategy,
uint256 strategyDebtRatio,
uint256 strategyMaxDebtPerHarvest,
uint256 strategyMinDebtPerHarvest,
uint256 strategyPerformanceFee
) external;
function revokeStrategy(address strategy) external;
function removeStrategy(address strategy) external;
function updateStrategyData(
address strategy,
uint256 newDebtRatio,
uint256 newMaxDebtPerHarvest,
uint256 newMinDebtPerHarvest,
uint256 newPerformanceFee
) external;
function setWithdrawalQueue(address[20] calldata queue) external;
function setPerformanceFee(uint256 _performanceFee) external;
function setManagementFee(uint256 _managementFee) external;
function setLockedProfitDegradation(uint256 _lockedProfitDegradation) external;
function setDepositLimit(uint256 _depositLimit) external;
function setTreasury(address _treasury) external;
/// Vault view functions
function performanceFee() external returns (uint256);
function managementFee() external returns (uint256);
function lockedProfitDegradation() external view returns (uint256);
function depositLimit() external returns (uint256);
function MAXIMUM_STRATEGIES() external returns (uint256);
function DEGRADATION_COEFFICIENT() external view returns (uint256);
function shareValue(uint256 shares) external view returns (uint256);
function sharesForAmount(uint256 amount) external view returns (uint256 shares);
function debtOutstanding(address strategy) external view returns (uint256);
function totalAssets() external view returns (uint256);
function lastReport() external view returns (uint256);
function lockedProfit() external view returns (uint256);
function treasury() external view returns (address);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeTransferLib.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @dev Caution! This library won't check that a token has code, responsibility is delegated to the caller.
library SafeTransferLib {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The ETH transfer has failed.
error ETHTransferFailed();
/// @dev The ERC20 `transferFrom` has failed.
error TransferFromFailed();
/// @dev The ERC20 `transfer` has failed.
error TransferFailed();
/// @dev The ERC20 `approve` has failed.
error ApproveFailed();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Suggested gas stipend for contract receiving ETH
/// that disallows any storage writes.
uint256 internal constant _GAS_STIPEND_NO_STORAGE_WRITES = 2300;
/// @dev Suggested gas stipend for contract receiving ETH to perform a few
/// storage reads and writes, but low enough to prevent griefing.
/// Multiply by a small constant (e.g. 2), if needed.
uint256 internal constant _GAS_STIPEND_NO_GRIEF = 100000;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ETH OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Sends `amount` (in wei) ETH to `to`.
/// Reverts upon failure.
function safeTransferETH(address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
// Transfer the ETH and check if it succeeded or not.
if iszero(call(gas(), to, amount, 0, 0, 0, 0)) {
// Store the function selector of `ETHTransferFailed()`.
mstore(0x00, 0xb12d13eb)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
}
}
/// @dev Force sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
/// The `gasStipend` can be set to a low enough value to prevent
/// storage writes or gas griefing.
///
/// If sending via the normal procedure fails, force sends the ETH by
/// creating a temporary contract which uses `SELFDESTRUCT` to force send the ETH.
///
/// Reverts if the current contract has insufficient balance.
function forceSafeTransferETH(address to, uint256 amount, uint256 gasStipend) internal {
/// @solidity memory-safe-assembly
assembly {
// If insufficient balance, revert.
if lt(selfbalance(), amount) {
// Store the function selector of `ETHTransferFailed()`.
mstore(0x00, 0xb12d13eb)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
// Transfer the ETH and check if it succeeded or not.
if iszero(call(gasStipend, to, amount, 0, 0, 0, 0)) {
mstore(0x00, to) // Store the address in scratch space.
mstore8(0x0b, 0x73) // Opcode `PUSH20`.
mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
// We can directly use `SELFDESTRUCT` in the contract creation.
// Compatible with `SENDALL`: https://eips.ethereum.org/EIPS/eip-4758
if iszero(create(amount, 0x0b, 0x16)) {
// For better gas estimation.
if iszero(gt(gas(), 1000000)) { revert(0, 0) }
}
}
}
}
/// @dev Force sends `amount` (in wei) ETH to `to`, with a gas stipend
/// equal to `_GAS_STIPEND_NO_GRIEF`. This gas stipend is a reasonable default
/// for 99% of cases and can be overriden with the three-argument version of this
/// function if necessary.
///
/// If sending via the normal procedure fails, force sends the ETH by
/// creating a temporary contract which uses `SELFDESTRUCT` to force send the ETH.
///
/// Reverts if the current contract has insufficient balance.
function forceSafeTransferETH(address to, uint256 amount) internal {
// Manually inlined because the compiler doesn't inline functions with branches.
/// @solidity memory-safe-assembly
assembly {
// If insufficient balance, revert.
if lt(selfbalance(), amount) {
// Store the function selector of `ETHTransferFailed()`.
mstore(0x00, 0xb12d13eb)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
// Transfer the ETH and check if it succeeded or not.
if iszero(call(_GAS_STIPEND_NO_GRIEF, to, amount, 0, 0, 0, 0)) {
mstore(0x00, to) // Store the address in scratch space.
mstore8(0x0b, 0x73) // Opcode `PUSH20`.
mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
// We can directly use `SELFDESTRUCT` in the contract creation.
// Compatible with `SENDALL`: https://eips.ethereum.org/EIPS/eip-4758
if iszero(create(amount, 0x0b, 0x16)) {
// For better gas estimation.
if iszero(gt(gas(), 1000000)) { revert(0, 0) }
}
}
}
}
/// @dev Sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
/// The `gasStipend` can be set to a low enough value to prevent
/// storage writes or gas griefing.
///
/// Simply use `gasleft()` for `gasStipend` if you don't need a gas stipend.
///
/// Note: Does NOT revert upon failure.
/// Returns whether the transfer of ETH is successful instead.
function trySafeTransferETH(address to, uint256 amount, uint256 gasStipend)
internal
returns (bool success)
{
/// @solidity memory-safe-assembly
assembly {
// Transfer the ETH and check if it succeeded or not.
success := call(gasStipend, to, amount, 0, 0, 0, 0)
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ERC20 OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
/// Reverts upon failure.
///
/// The `from` account must have at least `amount` approved for
/// the current contract to manage.
function safeTransferFrom(address token, address from, address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x60, amount) // Store the `amount` argument.
mstore(0x40, to) // Store the `to` argument.
mstore(0x2c, shl(96, from)) // Store the `from` argument.
// Store the function selector of `transferFrom(address,address,uint256)`.
mstore(0x0c, 0x23b872dd000000000000000000000000)
if iszero(
and( // The arguments of `and` are evaluated from right to left.
// 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(eq(mload(0x00), 1), iszero(returndatasize())),
call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
)
) {
// Store the function selector of `TransferFromFailed()`.
mstore(0x00, 0x7939f424)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
mstore(0x60, 0) // Restore the zero slot to zero.
mstore(0x40, m) // Restore the free memory pointer.
}
}
/// @dev Sends all of ERC20 `token` from `from` to `to`.
/// Reverts upon failure.
///
/// The `from` account must have at least `amount` approved for
/// the current contract to manage.
function safeTransferAllFrom(address token, address from, address to)
internal
returns (uint256 amount)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x40, to) // Store the `to` argument.
mstore(0x2c, shl(96, from)) // Store the `from` argument.
// Store the function selector of `balanceOf(address)`.
mstore(0x0c, 0x70a08231000000000000000000000000)
if iszero(
and( // The arguments of `and` are evaluated from right to left.
gt(returndatasize(), 0x1f), // At least 32 bytes returned.
staticcall(gas(), token, 0x1c, 0x24, 0x60, 0x20)
)
) {
// Store the function selector of `TransferFromFailed()`.
mstore(0x00, 0x7939f424)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
// Store the function selector of `transferFrom(address,address,uint256)`.
mstore(0x00, 0x23b872dd)
// The `amount` argument is already written to the memory word at 0x6c.
amount := mload(0x60)
if iszero(
and( // The arguments of `and` are evaluated from right to left.
// 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(eq(mload(0x00), 1), iszero(returndatasize())),
call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
)
) {
// Store the function selector of `TransferFromFailed()`.
mstore(0x00, 0x7939f424)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
mstore(0x60, 0) // Restore the zero slot to zero.
mstore(0x40, m) // Restore the free memory pointer.
}
}
/// @dev Sends `amount` of ERC20 `token` from the current contract to `to`.
/// Reverts upon failure.
function safeTransfer(address token, address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
mstore(0x14, to) // Store the `to` argument.
mstore(0x34, amount) // Store the `amount` argument.
// Store the function selector of `transfer(address,uint256)`.
mstore(0x00, 0xa9059cbb000000000000000000000000)
if iszero(
and( // The arguments of `and` are evaluated from right to left.
// 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(eq(mload(0x00), 1), iszero(returndatasize())),
call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
)
) {
// Store the function selector of `TransferFailed()`.
mstore(0x00, 0x90b8ec18)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
// Restore the part of the free memory pointer that was overwritten.
mstore(0x34, 0)
}
}
/// @dev Sends all of ERC20 `token` from the current contract to `to`.
/// Reverts upon failure.
function safeTransferAll(address token, address to) internal returns (uint256 amount) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, 0x70a08231) // Store the function selector of `balanceOf(address)`.
mstore(0x20, address()) // Store the address of the current contract.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
gt(returndatasize(), 0x1f), // At least 32 bytes returned.
staticcall(gas(), token, 0x1c, 0x24, 0x34, 0x20)
)
) {
// Store the function selector of `TransferFailed()`.
mstore(0x00, 0x90b8ec18)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
mstore(0x14, to) // Store the `to` argument.
// The `amount` argument is already written to the memory word at 0x34.
amount := mload(0x34)
// Store the function selector of `transfer(address,uint256)`.
mstore(0x00, 0xa9059cbb000000000000000000000000)
if iszero(
and( // The arguments of `and` are evaluated from right to left.
// 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(eq(mload(0x00), 1), iszero(returndatasize())),
call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
)
) {
// Store the function selector of `TransferFailed()`.
mstore(0x00, 0x90b8ec18)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
// Restore the part of the free memory pointer that was overwritten.
mstore(0x34, 0)
}
}
/// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
/// Reverts upon failure.
function safeApprove(address token, address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
mstore(0x14, to) // Store the `to` argument.
mstore(0x34, amount) // Store the `amount` argument.
// Store the function selector of `approve(address,uint256)`.
mstore(0x00, 0x095ea7b3000000000000000000000000)
if iszero(
and( // The arguments of `and` are evaluated from right to left.
// 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(eq(mload(0x00), 1), iszero(returndatasize())),
call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
)
) {
// Store the function selector of `ApproveFailed()`.
mstore(0x00, 0x3e3f8f73)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
// Restore the part of the free memory pointer that was overwritten.
mstore(0x34, 0)
}
}
/// @dev Returns the amount of ERC20 `token` owned by `account`.
/// Returns zero if the `token` does not exist.
function balanceOf(address token, address account) internal view returns (uint256 amount) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x14, account) // Store the `account` argument.
// Store the function selector of `balanceOf(address)`.
mstore(0x00, 0x70a08231000000000000000000000000)
amount :=
mul(
mload(0x20),
and( // The arguments of `and` are evaluated from right to left.
gt(returndatasize(), 0x1f), // At least 32 bytes returned.
staticcall(gas(), token, 0x10, 0x24, 0x20, 0x20)
)
)
}
}
}
pragma solidity ^0.8.19;
/// @notice Stores all data from a single strategy
/// @dev Packed in two slots
struct StrategyData {
/// Slot 0
/// @notice Maximum percentage available to be lent to strategies(in BPS)
/// @dev in BPS. uint16 is enough to cover the max BPS value of 10_000
uint16 strategyDebtRatio;
/// @notice The performance fee
/// @dev in BPS. uint16 is enough to cover the max BPS value of 10_000
uint16 strategyPerformanceFee;
/// @notice Timestamp when the strategy was added.
/// @dev Overflowing July 21, 2554
uint48 strategyActivation;
/// @notice block.timestamp of the last time a report occured
/// @dev Overflowing July 21, 2554
uint48 strategyLastReport;
/// @notice Upper limit on the increase of debt since last harvest
/// @dev max debt per harvest to be set to a maximum value of 4,722,366,482,869,645,213,695
uint128 strategyMaxDebtPerHarvest;
/// Slot 1
/// @notice Lower limit on the increase of debt since last harvest
/// @dev min debt per harvest to be set to a maximum value of 16,777,215
uint128 strategyMinDebtPerHarvest;
/// @notice Total returns that Strategy has realized for Vault
/// @dev max strategy total gain of 79,228,162,514,264,337,593,543,950,335
uint128 strategyTotalGain;
/// Slot 2
/// @notice Total outstanding debt that Strategy has
/// @dev max total debt of 79,228,162,514,264,337,593,543,950,335
uint128 strategyTotalDebt;
/// @notice Total losses that Strategy has realized for Vault
/// @dev max strategy total loss of 79,228,162,514,264,337,593,543,950,335
uint128 strategyTotalLoss;
}