// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
library BoringMath {
error ErrOverflow();
function add(uint256 a, uint256 b) internal pure returns (uint256) {
return a + b;
}
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
return a - b;
}
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
return a * b;
}
function div(uint256 a, uint256 b) internal pure returns (uint256) {
return a / b;
}
function to32(uint256 a) internal pure returns (uint32) {
if (a > type(uint32).max) {
revert ErrOverflow();
}
return uint32(a);
}
function to40(uint256 a) internal pure returns (uint40) {
if (a > type(uint40).max) {
revert ErrOverflow();
}
return uint40(a);
}
function to64(uint256 a) internal pure returns (uint64) {
if (a > type(uint64).max) {
revert ErrOverflow();
}
return uint64(a);
}
function to112(uint256 a) internal pure returns (uint112) {
if (a > type(uint112).max) {
revert ErrOverflow();
}
return uint112(a);
}
function to128(uint256 a) internal pure returns (uint128) {
if (a > type(uint128).max) {
revert ErrOverflow();
}
return uint128(a);
}
function to208(uint256 a) internal pure returns (uint208) {
if (a > type(uint208).max) {
revert ErrOverflow();
}
return uint208(a);
}
function to216(uint256 a) internal pure returns (uint216) {
if (a > type(uint216).max) {
revert ErrOverflow();
}
return uint216(a);
}
function to224(uint256 a) internal pure returns (uint224) {
if (a > type(uint224).max) {
revert ErrOverflow();
}
return uint224(a);
}
}
library BoringMath32 {
function add(uint32 a, uint32 b) internal pure returns (uint32) {
return a + b;
}
function sub(uint32 a, uint32 b) internal pure returns (uint32) {
return a - b;
}
function mul(uint32 a, uint32 b) internal pure returns (uint32) {
return a * b;
}
function div(uint32 a, uint32 b) internal pure returns (uint32) {
return a / b;
}
}
library BoringMath64 {
function add(uint64 a, uint64 b) internal pure returns (uint64) {
return a + b;
}
function sub(uint64 a, uint64 b) internal pure returns (uint64) {
return a - b;
}
function mul(uint64 a, uint64 b) internal pure returns (uint64) {
return a * b;
}
function div(uint64 a, uint64 b) internal pure returns (uint64) {
return a / b;
}
}
library BoringMath112 {
function add(uint112 a, uint112 b) internal pure returns (uint112) {
return a + b;
}
function sub(uint112 a, uint112 b) internal pure returns (uint112) {
return a - b;
}
function mul(uint112 a, uint112 b) internal pure returns (uint112) {
return a * b;
}
function div(uint112 a, uint112 b) internal pure returns (uint112) {
return a / b;
}
}
library BoringMath128 {
function add(uint128 a, uint128 b) internal pure returns (uint128) {
return a + b;
}
function sub(uint128 a, uint128 b) internal pure returns (uint128) {
return a - b;
}
function mul(uint128 a, uint128 b) internal pure returns (uint128) {
return a * b;
}
function div(uint128 a, uint128 b) internal pure returns (uint128) {
return a / b;
}
}
library BoringMath224 {
function add(uint224 a, uint224 b) internal pure returns (uint224) {
return a + b;
}
function sub(uint224 a, uint224 b) internal pure returns (uint224) {
return a - b;
}
function mul(uint224 a, uint224 b) internal pure returns (uint224) {
return a * b;
}
function div(uint224 a, uint224 b) internal pure returns (uint224) {
return a / b;
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {BoringMath, BoringMath128} from "./BoringMath.sol";
struct Rebase {
uint128 elastic;
uint128 base;
}
/// @notice A rebasing library using overflow-/underflow-safe math.
library RebaseLibrary {
using BoringMath for uint256;
using BoringMath128 for uint128;
/// @notice Calculates the base value in relationship to `elastic` and `total`.
function toBase(
Rebase memory total,
uint256 elastic,
bool roundUp
) internal pure returns (uint256 base) {
if (total.elastic == 0) {
base = elastic;
} else {
base = (elastic * total.base) / total.elastic;
if (roundUp && (base * total.elastic) / total.base < elastic) {
base++;
}
}
}
/// @notice Calculates the elastic value in relationship to `base` and `total`.
function toElastic(
Rebase memory total,
uint256 base,
bool roundUp
) internal pure returns (uint256 elastic) {
if (total.base == 0) {
elastic = base;
} else {
elastic = (base * total.elastic) / total.base;
if (roundUp && (elastic * total.base) / total.elastic < base) {
elastic++;
}
}
}
/// @notice Add `elastic` to `total` and doubles `total.base`.
/// @return (Rebase) The new total.
/// @return base in relationship to `elastic`.
function add(
Rebase memory total,
uint256 elastic,
bool roundUp
) internal pure returns (Rebase memory, uint256 base) {
base = toBase(total, elastic, roundUp);
total.elastic += elastic.to128();
total.base += base.to128();
return (total, base);
}
/// @notice Sub `base` from `total` and update `total.elastic`.
/// @return (Rebase) The new total.
/// @return elastic in relationship to `base`.
function sub(
Rebase memory total,
uint256 base,
bool roundUp
) internal pure returns (Rebase memory, uint256 elastic) {
elastic = toElastic(total, base, roundUp);
total.elastic -= elastic.to128();
total.base -= base.to128();
return (total, elastic);
}
/// @notice Add `elastic` and `base` to `total`.
function add(
Rebase memory total,
uint256 elastic,
uint256 base
) internal pure returns (Rebase memory) {
total.elastic += elastic.to128();
total.base += base.to128();
return total;
}
/// @notice Subtract `elastic` and `base` to `total`.
function sub(
Rebase memory total,
uint256 elastic,
uint256 base
) internal pure returns (Rebase memory) {
total.elastic -= elastic.to128();
total.base -= base.to128();
return total;
}
/// @notice Add `elastic` to `total` and update storage.
/// @return newElastic Returns updated `elastic`.
function addElastic(Rebase storage total, uint256 elastic) internal returns (uint256 newElastic) {
newElastic = total.elastic += elastic.to128();
}
/// @notice Subtract `elastic` from `total` and update storage.
/// @return newElastic Returns updated `elastic`.
function subElastic(Rebase storage total, uint256 elastic) internal returns (uint256 newElastic) {
newElastic = total.elastic -= elastic.to128();
}
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.8.0;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeTransferLib} from "@solady/utils/SafeTransferLib.sol";
import {IBentoBoxLite} from "/interfaces/IBentoBoxV1.sol";
import {ILevSwapperV2} from "/interfaces/ILevSwapperV2.sol";
import {IERC4626} from "/interfaces/IERC4626.sol";
contract ERC4626LevSwapper is ILevSwapperV2 {
using SafeTransferLib for address;
error ErrSwapFailed();
IBentoBoxLite public immutable box;
address public immutable mim;
address public immutable token;
IERC4626 public immutable vault;
constructor(IBentoBoxLite _box, IERC4626 _vault, address _mim) {
box = _box;
vault = _vault;
mim = _mim;
address _token = _vault.asset();
token = _token;
_token.safeApprove(address(_vault), type(uint256).max);
}
function swap(
address recipient,
uint256 shareToMin,
uint256 shareFrom,
bytes calldata data
) external override returns (uint256 extraShare, uint256 shareReturned) {
(address to, bytes memory swapData) = abi.decode(data, (address, bytes));
box.withdraw(mim, address(this), address(this), 0, shareFrom);
if (IERC20(mim).allowance(address(this), to) != type(uint256).max) {
mim.safeApprove(to, type(uint256).max);
}
// MIM -> Asset
(bool success, ) = to.call(swapData);
if (!success) {
revert ErrSwapFailed();
}
uint256 _amount = token.balanceOf(address(this));
_amount = vault.deposit(_amount, address(box));
// Refund remaining mim balance to the recipient
uint256 balance = mim.balanceOf(address(this));
if (balance > 0) {
mim.safeTransfer(recipient, balance);
}
(, shareReturned) = box.deposit(address(vault), address(box), recipient, _amount, 0);
extraShare = shareReturned - shareToMin;
}
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
import {IERC20} from "@BoringSolidity/interfaces/IERC20.sol";
import {Rebase} from "@BoringSolidity/libraries/BoringRebase.sol";
import {IStrategy} from "/interfaces/IStrategy.sol";
interface IFlashBorrower {
/// @notice The flashloan callback. `amount` + `fee` needs to repayed to msg.sender before this call returns.
/// @param sender The address of the invoker of this flashloan.
/// @param token The address of the token that is loaned.
/// @param amount of the `token` that is loaned.
/// @param fee The fee that needs to be paid on top for this loan. Needs to be the same as `token`.
/// @param data Additional data that was passed to the flashloan function.
function onFlashLoan(address sender, IERC20 token, uint256 amount, uint256 fee, bytes calldata data) external;
}
interface IBatchFlashBorrower {
/// @notice The callback for batched flashloans. Every amount + fee needs to repayed to msg.sender before this call returns.
/// @param sender The address of the invoker of this flashloan.
/// @param tokens Array of addresses for ERC-20 tokens that is loaned.
/// @param amounts A one-to-one map to `tokens` that is loaned.
/// @param fees A one-to-one map to `tokens` that needs to be paid on top for each loan. Needs to be the same token.
/// @param data Additional data that was passed to the flashloan function.
function onBatchFlashLoan(
address sender,
IERC20[] calldata tokens,
uint256[] calldata amounts,
uint256[] calldata fees,
bytes calldata data
) external;
}
interface IBentoBoxV1 {
function balanceOf(IERC20, address) external view returns (uint256);
function batch(bytes[] calldata calls, bool revertOnFail) external payable returns (bool[] memory successes, bytes[] memory results);
function batchFlashLoan(
IBatchFlashBorrower borrower,
address[] calldata receivers,
IERC20[] calldata tokens,
uint256[] calldata amounts,
bytes calldata data
) external;
function claimOwnership() external;
function flashLoan(IFlashBorrower borrower, address receiver, IERC20 token, uint256 amount, bytes calldata data) external;
function deploy(address masterContract, bytes calldata data, bool useCreate2) external payable returns (address);
function deposit(
IERC20 token_,
address from,
address to,
uint256 amount,
uint256 share
) external payable returns (uint256 amountOut, uint256 shareOut);
function harvest(IERC20 token, bool balance, uint256 maxChangeAmount) external;
function masterContractApproved(address, address) external view returns (bool);
function masterContractOf(address) external view returns (address);
function nonces(address) external view returns (uint256);
function owner() external view returns (address);
function pendingOwner() external view returns (address);
function pendingStrategy(IERC20) external view returns (IStrategy);
function permitToken(IERC20 token, address from, address to, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external;
function registerProtocol() external;
function setMasterContractApproval(address user, address masterContract, bool approved, uint8 v, bytes32 r, bytes32 s) external;
function setStrategy(IERC20 token, IStrategy newStrategy) external;
function setStrategyTargetPercentage(IERC20 token, uint64 targetPercentage_) external;
function strategy(IERC20) external view returns (IStrategy);
function strategyData(IERC20) external view returns (uint64 strategyStartDate, uint64 targetPercentage, uint128 balance);
function toAmount(IERC20 token, uint256 share, bool roundUp) external view returns (uint256 amount);
function toShare(IERC20 token, uint256 amount, bool roundUp) external view returns (uint256 share);
function totals(IERC20) external view returns (Rebase memory totals_);
function transfer(IERC20 token, address from, address to, uint256 share) external;
function transferMultiple(IERC20 token, address from, address[] calldata tos, uint256[] calldata shares) external;
function transferOwnership(address newOwner, bool direct, bool renounce) external;
function whitelistMasterContract(address masterContract, bool approved) external;
function whitelistedMasterContracts(address) external view returns (bool);
function withdraw(
IERC20 token_,
address from,
address to,
uint256 amount,
uint256 share
) external returns (uint256 amountOut, uint256 shareOut);
}
interface IBentoBoxLite {
function deposit(
address token_,
address from,
address to,
uint256 amount,
uint256 share
) external payable returns (uint256 amountOut, uint256 shareOut);
function withdraw(
address token_,
address from,
address to,
uint256 amount,
uint256 share
) external returns (uint256 amountOut, uint256 shareOut);
function balanceOf(address, address) external view returns (uint256);
function toAmount(address token, uint256 share, bool roundUp) external view returns (uint256 amount);
function toShare(address token, uint256 amount, bool roundUp) external view returns (uint256 share);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
/// @notice Tokenized Vaults with a single underlying EIP-20 token.
interface IERC4626 {
function decimals() external view returns (uint8 decimals);
/// @notice The address of the underlying token used for the Vault for accounting, depositing, and withdrawing.
function asset() external view returns (address assetTokenAddress);
/// @notice Total amount of the underlying asset that is “managed” by Vault.
function totalAssets() external view returns (uint256 totalManagedAssets);
/// @notice The amount of shares that the Vault would exchange for the amount of assets provided, in an ideal scenario where all the conditions are met.
function convertToShares(uint256 assets) external view returns (uint256 shares);
/// @notice The amount of assets that the Vault would exchange for the amount of shares provided, in an ideal scenario where all the conditions are met.
function convertToAssets(uint256 shares) external view returns (uint256 assets);
/// @notice Maximum amount of the underlying asset that can be deposited into the Vault for the receiver, through a deposit call.
function maxDeposit(address receiver) external view returns (uint256 maxAssets);
/// @notice Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given current on-chain conditions.
function previewDeposit(uint256 assets) external view returns (uint256 shares);
/// @notice Mints shares Vault shares to receiver by depositing exactly assets of underlying tokens.
function deposit(uint256 assets, address receiver) external returns (uint256 shares);
/// @notice Maximum amount of shares that can be minted from the Vault for the receiver, through a mint call.
function maxMint(address receiver) external view returns (uint256 maxShares);
/// @notice Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given current on-chain conditions.
function previewMint(uint256 shares) external view returns (uint256 assets);
/// @notice Mints exactly shares Vault shares to receiver by depositing assets of underlying tokens.
function mint(uint256 shares, address receiver) external returns (uint256 assets);
/// @notice Maximum amount of the underlying asset that can be withdrawn from the owner balance in the Vault, through a withdraw call.
function maxWithdraw(address owner) external view returns (uint256 maxAssets);
/// @notice Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block, given current on-chain conditions.
function previewWithdraw(uint256 assets) external view returns (uint256 shares);
/// @notice Burns shares from owner and sends exactly assets of underlying tokens to receiver.
function withdraw(
uint256 assets,
address receiver,
address owner
) external returns (uint256 shares);
/// @notice Maximum amount of Vault shares that can be redeemed from the owner balance in the Vault, through a redeem call.
function maxRedeem(address owner) external view returns (uint256 maxShares);
/// @notice Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block, given current on-chain conditions.
function previewRedeem(uint256 shares) external view returns (uint256 assets);
/// @notice Burns exactly shares from owner and sends assets of underlying tokens to receiver.
function redeem(
uint256 shares,
address receiver,
address owner
) external returns (uint256 assets);
event Deposit(address indexed caller, address indexed owner, uint256 assets, uint256 shares);
event Withdraw(address indexed caller, address indexed receiver, address indexed owner, uint256 assets, uint256 shares);
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
interface ILevSwapperV2 {
function swap(
address recipient,
uint256 shareToMin,
uint256 shareFrom,
bytes calldata data
) external returns (uint256 extraShare, uint256 shareReturned);
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
interface IStrategy {
function skim(uint256 amount) external;
function harvest(uint256 balance, address sender) external returns (int256 amountAdded);
function withdraw(uint256 amount) external returns (uint256 actualAmount);
function exit(uint256 balance) external returns (int256 amountAdded);
}
// 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)
/// @author Permit2 operations from (https://github.com/Uniswap/permit2/blob/main/src/libraries/Permit2Lib.sol)
///
/// @dev Note:
/// - For ETH transfers, please use `forceSafeTransferETH` for DoS protection.
/// - For ERC20s, this implementation 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();
/// @dev The Permit2 operation has failed.
error Permit2Failed();
/// @dev The Permit2 amount must be less than `2**160 - 1`.
error Permit2AmountOverflow();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* 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.
uint256 internal constant GAS_STIPEND_NO_GRIEF = 100000;
/// @dev The unique EIP-712 domain domain separator for the DAI token contract.
bytes32 internal constant DAI_DOMAIN_SEPARATOR =
0xdbb8cf42e1ecb028be3f3dbc922e1d878b963f411dc388ced501601c60f7c6f7;
/// @dev The address for the WETH9 contract on Ethereum mainnet.
address internal constant WETH9 = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
/// @dev The canonical Permit2 address.
/// [Github](https://github.com/Uniswap/permit2)
/// [Etherscan](https://etherscan.io/address/0x000000000022D473030F116dDEE9F6B43aC78BA3)
address internal constant PERMIT2 = 0x000000000022D473030F116dDEE9F6B43aC78BA3;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ETH OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// If the ETH transfer MUST succeed with a reasonable gas budget, use the force variants.
//
// The regular variants:
// - Forwards all remaining gas to the target.
// - Reverts if the target reverts.
// - Reverts if the current contract has insufficient balance.
//
// The force variants:
// - Forwards with an optional gas stipend
// (defaults to `GAS_STIPEND_NO_GRIEF`, which is sufficient for most cases).
// - If the target reverts, or if the gas stipend is exhausted,
// creates a temporary contract to force send the ETH via `SELFDESTRUCT`.
// Future compatible with `SENDALL`: https://eips.ethereum.org/EIPS/eip-4758.
// - Reverts if the current contract has insufficient balance.
//
// The try variants:
// - Forwards with a mandatory gas stipend.
// - Instead of reverting, returns whether the transfer succeeded.
/// @dev Sends `amount` (in wei) ETH to `to`.
function safeTransferETH(address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
if iszero(call(gas(), to, amount, codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Sends all the ETH in the current contract to `to`.
function safeTransferAllETH(address to) internal {
/// @solidity memory-safe-assembly
assembly {
// Transfer all the ETH and check if it succeeded or not.
if iszero(call(gas(), to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Force sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
function forceSafeTransferETH(address to, uint256 amount, uint256 gasStipend) internal {
/// @solidity memory-safe-assembly
assembly {
if lt(selfbalance(), amount) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
if iszero(call(gasStipend, to, amount, codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, to) // Store the address in scratch space.
mstore8(0x0b, 0x73) // Opcode `PUSH20`.
mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
if iszero(create(amount, 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
}
}
}
/// @dev Force sends all the ETH in the current contract to `to`, with a `gasStipend`.
function forceSafeTransferAllETH(address to, uint256 gasStipend) internal {
/// @solidity memory-safe-assembly
assembly {
if iszero(call(gasStipend, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, to) // Store the address in scratch space.
mstore8(0x0b, 0x73) // Opcode `PUSH20`.
mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
if iszero(create(selfbalance(), 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
}
}
}
/// @dev Force sends `amount` (in wei) ETH to `to`, with `GAS_STIPEND_NO_GRIEF`.
function forceSafeTransferETH(address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
if lt(selfbalance(), amount) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
if iszero(call(GAS_STIPEND_NO_GRIEF, to, amount, codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, to) // Store the address in scratch space.
mstore8(0x0b, 0x73) // Opcode `PUSH20`.
mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
if iszero(create(amount, 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
}
}
}
/// @dev Force sends all the ETH in the current contract to `to`, with `GAS_STIPEND_NO_GRIEF`.
function forceSafeTransferAllETH(address to) internal {
/// @solidity memory-safe-assembly
assembly {
// forgefmt: disable-next-item
if iszero(call(GAS_STIPEND_NO_GRIEF, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, to) // Store the address in scratch space.
mstore8(0x0b, 0x73) // Opcode `PUSH20`.
mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
if iszero(create(selfbalance(), 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
}
}
}
/// @dev Sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
function trySafeTransferETH(address to, uint256 amount, uint256 gasStipend)
internal
returns (bool success)
{
/// @solidity memory-safe-assembly
assembly {
success := call(gasStipend, to, amount, codesize(), 0x00, codesize(), 0x00)
}
}
/// @dev Sends all the ETH in the current contract to `to`, with a `gasStipend`.
function trySafeTransferAllETH(address to, uint256 gasStipend)
internal
returns (bool success)
{
/// @solidity memory-safe-assembly
assembly {
success := call(gasStipend, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* 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.
mstore(0x0c, 0x23b872dd000000000000000000000000) // `transferFrom(address,address,uint256)`.
// Perform the transfer, reverting upon failure.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
)
) {
mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
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 `from` to `to`.
///
/// The `from` account must have at least `amount` approved for the current contract to manage.
function trySafeTransferFrom(address token, address from, address to, uint256 amount)
internal
returns (bool success)
{
/// @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.
mstore(0x0c, 0x23b872dd000000000000000000000000) // `transferFrom(address,address,uint256)`.
success :=
and( // The arguments of `and` are evaluated from right to left.
or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
)
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 their entire balance 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.
mstore(0x0c, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
// Read the balance, reverting upon failure.
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)
)
) {
mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
revert(0x1c, 0x04)
}
mstore(0x00, 0x23b872dd) // `transferFrom(address,address,uint256)`.
amount := mload(0x60) // The `amount` is already at 0x60. We'll need to return it.
// Perform the transfer, reverting upon failure.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
)
) {
mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
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.
mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.
// Perform the transfer, reverting upon failure.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
)
) {
mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
revert(0x1c, 0x04)
}
mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
}
}
/// @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.
// Read the balance, reverting upon failure.
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)
)
) {
mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
revert(0x1c, 0x04)
}
mstore(0x14, to) // Store the `to` argument.
amount := mload(0x34) // The `amount` is already at 0x34. We'll need to return it.
mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.
// Perform the transfer, reverting upon failure.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
)
) {
mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
revert(0x1c, 0x04)
}
mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
}
}
/// @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.
mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
// Perform the approval, reverting upon failure.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
)
) {
mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.
revert(0x1c, 0x04)
}
mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
}
}
/// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
/// If the initial attempt to approve fails, attempts to reset the approved amount to zero,
/// then retries the approval again (some tokens, e.g. USDT, requires this).
/// Reverts upon failure.
function safeApproveWithRetry(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.
mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
// Perform the approval, retrying upon failure.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
)
) {
mstore(0x34, 0) // Store 0 for the `amount`.
mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
pop(call(gas(), token, 0, 0x10, 0x44, codesize(), 0x00)) // Reset the approval.
mstore(0x34, amount) // Store back the original `amount`.
// Retry the approval, reverting upon failure.
if iszero(
and(
or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
)
) {
mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.
revert(0x1c, 0x04)
}
}
mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
}
}
/// @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.
mstore(0x00, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
amount :=
mul( // The arguments of `mul` are evaluated from right to left.
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)
)
)
}
}
/// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
/// If the initial attempt fails, try to use Permit2 to transfer the token.
/// Reverts upon failure.
///
/// The `from` account must have at least `amount` approved for the current contract to manage.
function safeTransferFrom2(address token, address from, address to, uint256 amount) internal {
if (!trySafeTransferFrom(token, from, to, amount)) {
permit2TransferFrom(token, from, to, amount);
}
}
/// @dev Sends `amount` of ERC20 `token` from `from` to `to` via Permit2.
/// Reverts upon failure.
function permit2TransferFrom(address token, address from, address to, uint256 amount)
internal
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(add(m, 0x74), shr(96, shl(96, token)))
mstore(add(m, 0x54), amount)
mstore(add(m, 0x34), to)
mstore(add(m, 0x20), shl(96, from))
// `transferFrom(address,address,uint160,address)`.
mstore(m, 0x36c78516000000000000000000000000)
let p := PERMIT2
let exists := eq(chainid(), 1)
if iszero(exists) { exists := iszero(iszero(extcodesize(p))) }
if iszero(and(call(gas(), p, 0, add(m, 0x10), 0x84, codesize(), 0x00), exists)) {
mstore(0x00, 0x7939f4248757f0fd) // `TransferFromFailed()` or `Permit2AmountOverflow()`.
revert(add(0x18, shl(2, iszero(iszero(shr(160, amount))))), 0x04)
}
}
}
/// @dev Permit a user to spend a given amount of
/// another user's tokens via native EIP-2612 permit if possible, falling
/// back to Permit2 if native permit fails or is not implemented on the token.
function permit2(
address token,
address owner,
address spender,
uint256 amount,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
bool success;
/// @solidity memory-safe-assembly
assembly {
for {} shl(96, xor(token, WETH9)) {} {
mstore(0x00, 0x3644e515) // `DOMAIN_SEPARATOR()`.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
lt(iszero(mload(0x00)), eq(returndatasize(), 0x20)), // Returns 1 non-zero word.
// Gas stipend to limit gas burn for tokens that don't refund gas when
// an non-existing function is called. 5K should be enough for a SLOAD.
staticcall(5000, token, 0x1c, 0x04, 0x00, 0x20)
)
) { break }
// After here, we can be sure that token is a contract.
let m := mload(0x40)
mstore(add(m, 0x34), spender)
mstore(add(m, 0x20), shl(96, owner))
mstore(add(m, 0x74), deadline)
if eq(mload(0x00), DAI_DOMAIN_SEPARATOR) {
mstore(0x14, owner)
mstore(0x00, 0x7ecebe00000000000000000000000000) // `nonces(address)`.
mstore(add(m, 0x94), staticcall(gas(), token, 0x10, 0x24, add(m, 0x54), 0x20))
mstore(m, 0x8fcbaf0c000000000000000000000000) // `IDAIPermit.permit`.
// `nonces` is already at `add(m, 0x54)`.
// `1` is already stored at `add(m, 0x94)`.
mstore(add(m, 0xb4), and(0xff, v))
mstore(add(m, 0xd4), r)
mstore(add(m, 0xf4), s)
success := call(gas(), token, 0, add(m, 0x10), 0x104, codesize(), 0x00)
break
}
mstore(m, 0xd505accf000000000000000000000000) // `IERC20Permit.permit`.
mstore(add(m, 0x54), amount)
mstore(add(m, 0x94), and(0xff, v))
mstore(add(m, 0xb4), r)
mstore(add(m, 0xd4), s)
success := call(gas(), token, 0, add(m, 0x10), 0xe4, codesize(), 0x00)
break
}
}
if (!success) simplePermit2(token, owner, spender, amount, deadline, v, r, s);
}
/// @dev Simple permit on the Permit2 contract.
function simplePermit2(
address token,
address owner,
address spender,
uint256 amount,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
mstore(m, 0x927da105) // `allowance(address,address,address)`.
{
let addressMask := shr(96, not(0))
mstore(add(m, 0x20), and(addressMask, owner))
mstore(add(m, 0x40), and(addressMask, token))
mstore(add(m, 0x60), and(addressMask, spender))
mstore(add(m, 0xc0), and(addressMask, spender))
}
let p := mul(PERMIT2, iszero(shr(160, amount)))
if iszero(
and( // The arguments of `and` are evaluated from right to left.
gt(returndatasize(), 0x5f), // Returns 3 words: `amount`, `expiration`, `nonce`.
staticcall(gas(), p, add(m, 0x1c), 0x64, add(m, 0x60), 0x60)
)
) {
mstore(0x00, 0x6b836e6b8757f0fd) // `Permit2Failed()` or `Permit2AmountOverflow()`.
revert(add(0x18, shl(2, iszero(p))), 0x04)
}
mstore(m, 0x2b67b570) // `Permit2.permit` (PermitSingle variant).
// `owner` is already `add(m, 0x20)`.
// `token` is already at `add(m, 0x40)`.
mstore(add(m, 0x60), amount)
mstore(add(m, 0x80), 0xffffffffffff) // `expiration = type(uint48).max`.
// `nonce` is already at `add(m, 0xa0)`.
// `spender` is already at `add(m, 0xc0)`.
mstore(add(m, 0xe0), deadline)
mstore(add(m, 0x100), 0x100) // `signature` offset.
mstore(add(m, 0x120), 0x41) // `signature` length.
mstore(add(m, 0x140), r)
mstore(add(m, 0x160), s)
mstore(add(m, 0x180), shl(248, v))
if iszero(call(gas(), p, 0, add(m, 0x1c), 0x184, codesize(), 0x00)) {
mstore(0x00, 0x6b836e6b) // `Permit2Failed()`.
revert(0x1c, 0x04)
}
}
}
}
{
"compilationTarget": {
"src/swappers/ERC4626LevSwapper.sol": "ERC4626LevSwapper"
},
"evmVersion": "shanghai",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 400
},
"remappings": [
":/=src/",
":@BoringSolidity/=lib/BoringSolidity/contracts/",
":@excessivelySafeCall/=lib/ExcessivelySafeCall/src/",
":@fuzzlib/=lib/fuzzlib/src/",
":@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
":@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
":@safe-contracts/=lib/safe-contracts/contracts/",
":@solady/=lib/solady/src/",
":@solmate/=lib/solmate/src/",
":forge-std/=lib/forge-std/src/"
]
}
[{"inputs":[{"internalType":"contract IBentoBoxLite","name":"_box","type":"address"},{"internalType":"contract IERC4626","name":"_vault","type":"address"},{"internalType":"address","name":"_mim","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ErrSwapFailed","type":"error"},{"inputs":[],"name":"box","outputs":[{"internalType":"contract IBentoBoxLite","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mim","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"shareToMin","type":"uint256"},{"internalType":"uint256","name":"shareFrom","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"swap","outputs":[{"internalType":"uint256","name":"extraShare","type":"uint256"},{"internalType":"uint256","name":"shareReturned","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"token","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vault","outputs":[{"internalType":"contract IERC4626","name":"","type":"address"}],"stateMutability":"view","type":"function"}]