Description:
Multi-signature wallet contract requiring multiple confirmations for transaction execution.
Blockchain: Ethereum
Source Code: View Code On The Blockchain
Solidity Source Code:
{{
"language": "Solidity",
"sources": {
"src/micro-managers/PrvlFlashloanAaveBorrowV3.sol": {
"content": "// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.21;
import {UManager, FixedPointMathLib, ManagerWithMerkleVerification, ERC20} from "src/micro-managers/UManager.sol";
import {IUniswapV3Router} from "src/interfaces/IUniswapV3Router.sol";
import {DecoderCustomTypes} from "src/interfaces/DecoderCustomTypes.sol";
import {BalancerVault} from "src/interfaces/BalancerVault.sol";
import {BoringVault} from "src/base/BoringVault.sol";
import {Auth, Authority} from "@solmate/auth/Auth.sol";
interface IQuoter {
function quoteExactInputSingle(
address tokenIn,
address tokenOut,
uint24 fee,
uint256 amountIn,
uint160 sqrtPriceLimitX96
) external returns (uint256 amountOut);
function quoteExactOutputSingle(
address tokenIn,
address tokenOut,
uint24 fee,
uint256 amountOut,
uint160 sqrtPriceLimitX96
) external returns (uint256 amountIn);
}
interface IAavePool {
function getUserAccountData(address user) external view returns (
uint256 totalCollateralBase,
uint256 totalDebtBase,
uint256 availableBorrowsBase,
uint256 currentLiquidationThreshold,
uint256 ltv,
uint256 healthFactor
);
}
contract PrvlFlashloanAaveBorrowV3 is UManager {
using FixedPointMathLib for uint256;
/*
* @dev V3 removes hardcoded values
*/
IUniswapV3Router public immutable uniswapV3Router;
IQuoter public immutable quoter;
BalancerVault public immutable balancerVault;
address public immutable AAVE;
address public immutable baseToken; // eg depositToken
address public immutable depositToken; // eg wstdepositToken
address public immutable aToken; // eg awstETH
uint24 public immutable uniFeeTier;
uint256 public immutable aaveVariableRate;
bytes4 constant EXACT_INPUT_SINGLE_SELECTOR = 0x04e45aaf;
bytes4 constant SUPPLY_SELECTOR = 0x617ba037;
bytes4 constant BORROW_SELECTOR = 0xa415bcad;
bytes4 constant FLASHLOAN_SELECTOR = 0x5c38449e;
bytes4 constant REPAY_SELECTOR = 0x573ade81;
bytes4 constant WITHDRAW_SELECTOR = 0x69328dec;
bytes32[][] private borrowInnerManageProofs;
address[] private borrowInnerDecodersAndSanitizers;
bytes32[][] private repayInnerManageProofs;
address[] private repayInnerDecodersAndSanitizers;
address[] private outerDecodersAndSanitizers;
bytes32[][] private outerManageProofs;
constructor(
address _owner,
address _manager,
address _boringVault,
address _balancerVault,
address _uniswapV3Router,
address _quoter,
address _aave,
address _baseToken,
address _depositToken,
address _aToken,
uint24 _uniFeeTier,
uint256 _aaveVariableRate
) UManager(_owner, _manager, _boringVault) {
uniswapV3Router = IUniswapV3Router(_uniswapV3Router);
quoter = IQuoter(_quoter);
balancerVault = BalancerVault(_balancerVault);
AAVE = _aave;
baseToken = _baseToken;
depositToken = _depositToken;
aToken = _aToken;
uniFeeTier = _uniFeeTier;
aaveVariableRate = _aaveVariableRate;
}
error InsufficientVaultBalance();
function setBorrowInnerManageProofs(bytes32[][] calldata _borrowInnerManageProofs) external requiresAuth {
// Manual deep copy to avoid calldata-to-storage issue
delete borrowInnerManageProofs; // Clear existing
borrowInnerManageProofs = new bytes32[][](_borrowInnerManageProofs.length);
for (uint256 i = 0; i < _borrowInnerManageProofs.length; i++) {
borrowInnerManageProofs[i] = new bytes32[](_borrowInnerManageProofs[i].length);
for (uint256 j = 0; j < _borrowInnerManageProofs[i].length; j++) {
borrowInnerManageProofs[i][j] = _borrowInnerManageProofs[i][j];
}
}
}
function setBorrowInnerDecodersAndSanitizers(address[] calldata _borrowInnerDecodersAndSanitizers)
external
requiresAuth
{
// Simple array: manual copy
delete borrowInnerDecodersAndSanitizers;
borrowInnerDecodersAndSanitizers = new address[](_borrowInnerDecodersAndSanitizers.length);
for (uint256 i = 0; i < _borrowInnerDecodersAndSanitizers.length; i++) {
borrowInnerDecodersAndSanitizers[i] = _borrowInnerDecodersAndSanitizers[i];
}
}
function setRepayInnerManageProofs(bytes32[][] calldata _repayInnerManageProofs) external requiresAuth {
// Manual deep copy to avoid calldata-to-storage issue
delete repayInnerManageProofs; // Clear existing
repayInnerManageProofs = new bytes32[][](_repayInnerManageProofs.length);
for (uint256 i = 0; i < _repayInnerManageProofs.length; i++) {
repayInnerManageProofs[i] = new bytes32[](_repayInnerManageProofs[i].length);
for (uint256 j = 0; j < _repayInnerManageProofs[i].length; j++) {
repayInnerManageProofs[i][j] = _repayInnerManageProofs[i][j];
}
}
}
function setRepayInnerDecodersAndSanitizers(address[] calldata _repayInnerDecodersAndSanitizers)
external
requiresAuth
{
// Simple array: manual copy
delete repayInnerDecodersAndSanitizers;
repayInnerDecodersAndSanitizers = new address[](_repayInnerDecodersAndSanitizers.length);
for (uint256 i = 0; i < _repayInnerDecodersAndSanitizers.length; i++) {
repayInnerDecodersAndSanitizers[i] = _repayInnerDecodersAndSanitizers[i];
}
}
function setOuterDecodersAndSanitizers(address[] calldata _outerDecodersAndSanitizers) external requiresAuth {
// Simple array: manual copy
delete outerDecodersAndSanitizers;
outerDecodersAndSanitizers = new address[](_outerDecodersAndSanitizers.length);
for (uint256 i = 0; i < _outerDecodersAndSanitizers.length; i++) {
outerDecodersAndSanitizers[i] = _outerDecodersAndSanitizers[i];
}
}
function setOuterManageProofs(bytes32[][] calldata _outerManageProofs) external requiresAuth {
// Manual deep copy to avoid calldata-to-storage issue
delete outerManageProofs; // Clear existing
outerManageProofs = new bytes32[][](_outerManageProofs.length);
for (uint256 i = 0; i < _outerManageProofs.length; i++) {
outerManageProofs[i] = new bytes32[](_outerManageProofs[i].length);
for (uint256 j = 0; j < _outerManageProofs[i].length; j++) {
outerManageProofs[i][j] = _outerManageProofs[i][j];
}
}
}
function borrow(uint256 collateralAmount, uint256 borrowAmount) external requiresAuth {
uint256 vaultBalance = ERC20(baseToken).balanceOf(boringVault);
if (vaultBalance < collateralAmount) {
revert InsufficientVaultBalance();
}
bytes memory innerUserData = getBorrowUserData(collateralAmount, borrowAmount);
_executeFlashloan(innerUserData, borrowAmount);
}
function repay(uint256 borrowAmount, uint256 supplyAmount) external requiresAuth {
bytes memory innerUserData = getRepayUserData(borrowAmount, supplyAmount);
_executeFlashloan(innerUserData, borrowAmount);
}
function settle() external requiresAuth {
// Get total debt in USD (base currency)
(, uint256 totalDebtBase, , , , ) = IAavePool(AAVE).getUserAccountData(boringVault);
// Convert debt to baseToken amount (assuming 8 decimals for base currency, 6 for baseToken)
uint256 debtAmount = totalDebtBase / 100; // Convert from 8 decimals to 6 decimals
bytes memory innerUserData = getRepayUserData(type(uint256).max, type(uint256).max);
// Use actual debt amount for flashloan
_executeFlashloan(innerUserData, debtAmount);
}
function _executeFlashloan(bytes memory innerUserData, uint256 flashloanAmount) internal {
bytes32[][] memory outerProofs = outerManageProofs;
address[] memory outerTargets = new address[](1);
outerTargets[0] = address(manager);
uint256[] memory outerValues = new uint256[](1);
bytes[] memory outerTargetData = new bytes[](1);
address[] memory flashloanTokens = new address[](1);
flashloanTokens[0] = baseToken;
uint256[] memory flashloanAmounts = new uint256[](1);
flashloanAmounts[0] = flashloanAmount;
outerTargetData[0] = abi.encodeWithSelector(
ManagerWithMerkleVerification.flashLoan.selector,
address(manager),
flashloanTokens,
flashloanAmounts,
innerUserData
);
manager.manageVaultWithMerkleVerification(
outerProofs, outerDecodersAndSanitizers, outerTargets, outerTargetData, outerValues
);
}
function getBorrowUserData(uint256 collateralAmount, uint256 borrowAmount)
internal
returns (bytes memory userData)
{
uint256 swapAmount = collateralAmount + borrowAmount;
uint256 supplyAmount = quoter.quoteExactInputSingle(baseToken, depositToken, uniFeeTier, swapAmount, uint160(0));
bytes memory swapData = abi.encodeWithSelector(
EXACT_INPUT_SINGLE_SELECTOR, baseToken, depositToken, uniFeeTier, boringVault, swapAmount, 0, uint160(0)
);
bytes memory supplyData = abi.encodeWithSelector(SUPPLY_SELECTOR, depositToken, supplyAmount, boringVault, 0);
bytes memory borrowData =
abi.encodeWithSelector(BORROW_SELECTOR, baseToken, borrowAmount, aaveVariableRate, 0, boringVault);
address[] memory innerTargets = new address[](3);
innerTargets[0] = address(uniswapV3Router);
innerTargets[1] = AAVE;
innerTargets[2] = AAVE;
bytes[] memory innerTargetData = new bytes[](3);
innerTargetData[0] = swapData;
innerTargetData[1] = supplyData;
innerTargetData[2] = borrowData;
uint256[] memory innerValues = new uint256[](3);
userData = abi.encode(
borrowInnerManageProofs, borrowInnerDecodersAndSanitizers, innerTargets, innerTargetData, innerValues
);
}
function getRepayUserData(uint256 borrowAmount, uint256 supplyAmount) internal returns (bytes memory userData) {
bytes memory repayData =
abi.encodeWithSelector(REPAY_SELECTOR, baseToken, borrowAmount, aaveVariableRate, boringVault);
bytes memory withdrawData = abi.encodeWithSelector(WITHDRAW_SELECTOR, depositToken, supplyAmount, boringVault);
// For swap, use actual adepositToken balance if supplyAmount is max
uint256 swapAmount = supplyAmount;
if (supplyAmount == type(uint256).max) {
swapAmount = ERC20(aToken).balanceOf(boringVault);
}
bytes memory swapData = abi.encodeWithSelector(
EXACT_INPUT_SINGLE_SELECTOR, depositToken, baseToken, uniFeeTier, boringVault, swapAmount, 0, uint160(0)
);
address[] memory innerTargets = new address[](3);
innerTargets[0] = AAVE;
innerTargets[1] = AAVE;
innerTargets[2] = address(uniswapV3Router);
bytes[] memory innerTargetData = new bytes[](3);
innerTargetData[0] = repayData;
innerTargetData[1] = withdrawData;
innerTargetData[2] = swapData;
uint256[] memory innerValues = new uint256[](3);
userData = abi.encode(
repayInnerManageProofs, repayInnerDecodersAndSanitizers, innerTargets, innerTargetData, innerValues
);
}
}
"
},
"src/micro-managers/UManager.sol": {
"content": "// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.21;
import {FixedPointMathLib} from "@solmate/utils/FixedPointMathLib.sol";
import {ManagerWithMerkleVerification} from "src/base/Roles/ManagerWithMerkleVerification.sol";
import {ERC20} from "@solmate/tokens/ERC20.sol";
import {Auth, Authority} from "@solmate/auth/Auth.sol";
abstract contract UManager is Auth {
using FixedPointMathLib for uint256;
// ========================================= STATE =========================================
/**
* @notice The period in seconds for the rate limit.
*/
uint16 public period;
/**
* @notice The number of calls allowed per period.
*/
uint16 public allowedCallsPerPeriod;
/**
* @notice The number of calls made in the current period.
*/
mapping(uint256 => uint256) public callCountPerPeriod;
//============================== ERRORS ===============================
error UManager__CallCountExceeded();
//============================== EVENTS ===============================
event PeriodUpdated(uint16 oldPeriod, uint16 newPeriod);
event AllowedCallsPeriodUpdated(uint16 oldAllowance, uint16 newAllowance);
//============================== MODIFIERS ===============================
modifier enforceRateLimit() {
// Use parenthesis to avoid stack too deep error.
{
// We include this call in the current call count for period.
uint256 currentCallCountForPeriod = callCountPerPeriod[block.timestamp % period] + 1;
if (currentCallCountForPeriod > allowedCallsPerPeriod) {
revert UManager__CallCountExceeded();
}
callCountPerPeriod[block.timestamp % period] = currentCallCountForPeriod;
}
_;
}
//============================== IMMUTABLES ===============================
/**
* @notice The ManagerWithMerkleVerification this uManager works with.
*/
ManagerWithMerkleVerification internal immutable manager;
/**
* @notice The BoringVault this uManager works with.
*/
address internal immutable boringVault;
constructor(address _owner, address _manager, address _boringVault) Auth(_owner, Authority(address(0))) {
manager = ManagerWithMerkleVerification(_manager);
boringVault = _boringVault;
}
// ========================================= ADMIN FUNCTIONS =========================================
/**
* @notice Sets the duration of the period.
* @dev Callable by MULTISIG_ROLE.
*/
function setPeriod(uint16 _period) external requiresAuth {
emit PeriodUpdated(period, _period);
period = _period;
}
/**
* @notice Sets the number of calls allowed per period.
* @dev Callable by MULTISIG_ROLE.
*/
function setAllowedCallsPerPeriod(uint16 _allowedCallsPerPeriod) external requiresAuth {
emit AllowedCallsPeriodUpdated(allowedCallsPerPeriod, _allowedCallsPerPeriod);
allowedCallsPerPeriod = _allowedCallsPerPeriod;
}
/**
* @notice Allows auth to set token approvals to zero.
* @dev Callable by STRATEGIST_ROLE.
*/
function revokeTokenApproval(
bytes32[][] calldata manageProofs,
address[] calldata decodersAndSanitizers,
ERC20[] calldata tokens,
address[] calldata spenders
) external requiresAuth {
uint256 tokensLength = tokens.length;
address[] memory targets = new address[](tokensLength);
bytes[] memory targetData = new bytes[](tokensLength);
uint256[] memory values = new uint256[](tokensLength);
for (uint256 i; i < tokensLength; ++i) {
targets[i] = address(tokens[i]);
targetData[i] = abi.encodeWithSelector(ERC20.approve.selector, spenders[i], 0);
// values[i] = 0;
}
// Make the manage call.
manager.manageVaultWithMerkleVerification(manageProofs, decodersAndSanitizers, targets, targetData, values);
}
}
"
},
"src/interfaces/IUniswapV3Router.sol": {
"content": "// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.8.0;
/// @title Callback for IUniswapV3PoolActions#swap
/// @notice Any contract that calls IUniswapV3PoolActions#swap must implement this interface
interface IUniswapV3SwapCallback {
/// @notice Called to `msg.sender` after executing a swap via IUniswapV3Pool#swap.
/// @dev In the implementation you must pay the pool tokens owed for the swap.
/// The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory.
/// amount0Delta and amount1Delta can both be 0 if no tokens were swapped.
/// @param amount0Delta The amount of token0 that was sent (negative) or must be received (positive) by the pool by
/// the end of the swap. If positive, the callback must send that amount of token0 to the pool.
/// @param amount1Delta The amount of token1 that was sent (negative) or must be received (positive) by the pool by
/// the end of the swap. If positive, the callback must send that amount of token1 to the pool.
/// @param data Any data passed through by the caller via the IUniswapV3PoolActions#swap call
function uniswapV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata data) external;
}
/// @title Router token swapping functionality
/// @notice Functions for swapping tokens via Uniswap V3
interface IUniswapV3Router is IUniswapV3SwapCallback {
struct ExactInputSingleParams {
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 token
function exactInputSingle(ExactInputSingleParams calldata params) external payable returns (uint256 amountOut);
struct ExactInputParams {
bytes path;
address recipient;
uint256 deadline;
uint256 amountIn;
uint256 amountOutMinimum;
}
/// @notice Swaps `amountIn` of one token for as much as possible of another along the specified path
/// @param params The parameters necessary for the multi-hop swap, encoded as `ExactInputParams` in calldata
/// @return amountOut The amount of the received token
function exactInput(ExactInputParams calldata params) external payable returns (uint256 amountOut);
struct ExactOutputSingleParams {
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 token
function exactOutputSingle(ExactOutputSingleParams calldata params) external payable returns (uint256 amountIn);
struct ExactOutputParams {
bytes path;
address recipient;
uint256 deadline;
uint256 amountOut;
uint256 amountInMaximum;
}
/// @notice Swaps as little as possible of one token for `amountOut` of another along the specified path (reversed)
/// @param params The parameters necessary for the multi-hop swap, encoded as `ExactOutputParams` in calldata
/// @return amountIn The amount of the input token
function exactOutput(ExactOutputParams calldata params) external payable returns (uint256 amountIn);
}
"
},
"src/interfaces/DecoderCustomTypes.sol": {
"content": "// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.21;
contract DecoderCustomTypes {
// ========================================= BALANCER =========================================
struct JoinPoolRequest {
address[] assets;
uint256[] maxAmountsIn;
bytes userData;
bool fromInternalBalance;
}
struct ExitPoolRequest {
address[] assets;
uint256[] minAmountsOut;
bytes userData;
bool toInternalBalance;
}
enum SwapKind {
GIVEN_IN,
GIVEN_OUT
}
struct SingleSwap {
bytes32 poolId;
SwapKind kind;
address assetIn;
address assetOut;
uint256 amount;
bytes userData;
}
struct FundManagement {
address sender;
bool fromInternalBalance;
address recipient;
bool toInternalBalance;
}
// ========================================= UNISWAP V3 =========================================
struct MintParams {
address token0;
address token1;
uint24 fee;
int24 tickLower;
int24 tickUpper;
uint256 amount0Desired;
uint256 amount1Desired;
uint256 amount0Min;
uint256 amount1Min;
address recipient;
uint256 deadline;
}
struct IncreaseLiquidityParams {
uint256 tokenId;
uint256 amount0Desired;
uint256 amount1Desired;
uint256 amount0Min;
uint256 amount1Min;
uint256 deadline;
}
struct DecreaseLiquidityParams {
uint256 tokenId;
uint128 liquidity;
uint256 amount0Min;
uint256 amount1Min;
uint256 deadline;
}
struct CollectParams {
uint256 tokenId;
address recipient;
uint128 amount0Max;
uint128 amount1Max;
}
struct ExactInputParams {
bytes path;
address recipient;
uint256 deadline;
uint256 amountIn;
uint256 amountOutMinimum;
}
struct ExactInputParamsRouter02 {
bytes path;
address recipient;
uint256 amountIn;
uint256 amountOutMinimum;
}
struct PancakeSwapExactInputParams {
bytes path;
address recipient;
uint256 amountIn;
uint256 amountOutMinimum;
}
// ========================================= UNISWAP V4 =========================================
struct SwapParams {
/// Whether to swap token0 for token1 or vice versa
bool zeroForOne;
/// The desired input amount if negative (exactIn), or the desired output amount if positive (exactOut)
int256 amountSpecified;
/// The sqrt price at which, if reached, the swap will stop executing
uint160 sqrtPriceLimitX96;
}
struct PoolKey {
/// @notice The lower currency of the pool, sorted numerically
address currency0;
/// @notice The higher currency of the pool, sorted numerically
address currency1;
/// @notice The pool LP fee, capped at 1_000_000. If the highest bit is 1, the pool has a dynamic fee and must be exactly equal to 0x800000
uint24 fee;
/// @notice Ticks that involve positions must be a multiple of tick spacing
int24 tickSpacing;
/// @notice The hooks of the pool
address hooks;
}
/// @dev comes from IV4 Router
struct ExactInputSingleParams {
PoolKey poolKey;
bool zeroForOne;
uint128 amountIn;
uint128 amountOutMinimum;
bytes hookData;
}
/// @notice Parameters for a single-hop exact-output swap
struct ExactOutputSingleParams {
PoolKey poolKey;
bool zeroForOne;
uint128 amountOut;
uint128 amountInMaximum;
bytes hookData;
}
// ========================================= MORPHO BLUE =========================================
struct MarketParams {
address loanToken;
address collateralToken;
address oracle;
address irm;
uint256 lltv;
}
// ========================================= 1INCH =========================================
struct SwapDescription {
address srcToken;
address dstToken;
address payable srcReceiver;
address payable dstReceiver;
uint256 amount;
uint256 minReturnAmount;
uint256 flags;
}
// ========================================= PENDLE =========================================
struct TokenInput {
// TOKEN DATA
address tokenIn;
uint256 netTokenIn;
address tokenMintSy;
// AGGREGATOR DATA
address pendleSwap;
SwapData swapData;
}
struct TokenOutput {
// TOKEN DATA
address tokenOut;
uint256 minTokenOut;
address tokenRedeemSy;
// AGGREGATOR DATA
address pendleSwap;
SwapData swapData;
}
struct ApproxParams {
uint256 guessMin;
uint256 guessMax;
uint256 guessOffchain; // pass 0 in to skip this variable
uint256 maxIteration; // every iteration, the diff between guessMin and guessMax will be divided by 2
uint256 eps; // the max eps between the returned result & the correct result, base 1e18. Normally this number will be set
// to 1e15 (1e18/1000 = 0.1%)
}
struct SwapData {
SwapType swapType;
address extRouter;
bytes extCalldata;
bool needScale;
}
enum SwapType {
NONE,
KYBERSWAP,
ONE_INCH,
// ETH_WETH not used in Aggregator
ETH_WETH
}
struct LimitOrderData {
address limitRouter;
uint256 epsSkipMarket; // only used for swap operations, will be ignored otherwise
FillOrderParams[] normalFills;
FillOrderParams[] flashFills;
bytes optData;
}
struct FillOrderParams {
Order order;
bytes signature;
uint256 makingAmount;
}
struct Order {
uint256 salt;
uint256 expiry;
uint256 nonce;
OrderType orderType;
address token;
address YT;
address maker;
address receiver;
uint256 makingAmount;
uint256 lnImpliedRate;
uint256 failSafeRate;
bytes permit;
}
enum OrderType {
SY_FOR_PT,
PT_FOR_SY,
SY_FOR_YT,
YT_FOR_SY
}
// ========================================= EIGEN LAYER =========================================
struct QueuedWithdrawalParams {
// Array of strategies that the QueuedWithdrawal contains
address[] strategies;
// Array containing the amount of shares in each Strategy in the `strategies` array
uint256[] shares;
// The address of the withdrawer
address withdrawer;
}
struct Withdrawal {
// The address that originated the Withdrawal
address staker;
// The address that the staker was delegated to at the time that the Withdrawal was created
address delegatedTo;
// The address that can complete the Withdrawal + will receive funds when completing the withdrawal
address withdrawer;
// Nonce used to guarantee that otherwise identical withdrawals have unique hashes
uint256 nonce;
// Block number when the Withdrawal was created
uint32 startBlock;
// Array of strategies that the Withdrawal contains
address[] strategies;
// Array containing the amount of shares in each Strategy in the `strategies` array
uint256[] shares;
}
struct SignatureWithExpiry {
// the signature itself, formatted as a single bytes object
bytes signature;
// the expiration timestamp (UTC) of the signature
uint256 expiry;
}
struct EarnerTreeMerkleLeaf {
address earner;
bytes32 earnerTokenRoot;
}
struct TokenTreeMerkleLeaf {
address token;
uint256 cumulativeEarnings;
}
struct RewardsMerkleClaim {
uint32 rootIndex;
uint32 earnerIndex;
bytes earnerTreeProof;
EarnerTreeMerkleLeaf earnerLeaf;
uint32[] tokenIndices;
bytes[] tokenTreeProofs;
TokenTreeMerkleLeaf[] tokenLeaves;
}
// ========================================= CCIP =========================================
// If extraArgs is empty bytes, the default is 200k gas limit.
struct EVM2AnyMessage {
bytes receiver; // abi.encode(receiver address) for dest EVM chains
bytes data; // Data payload
EVMTokenAmount[] tokenAmounts; // Token transfers
address feeToken; // Address of feeToken. address(0) means you will send msg.value.
bytes extraArgs; // Populate this with _argsToBytes(EVMExtraArgsV2)
}
/// @dev RMN depends on this struct, if changing, please notify the RMN maintainers.
struct EVMTokenAmount {
address token; // token address on the local chain.
uint256 amount; // Amount of tokens.
}
struct EVMExtraArgsV1 {
uint256 gasLimit;
}
// ========================================= OFT =========================================
struct SendParam {
uint32 dstEid; // Destination endpoint ID.
bytes32 to; // Recipient address.
uint256 amountLD; // Amount to send in local decimals.
uint256 minAmountLD; // Minimum amount to send in local decimals.
bytes extraOptions; // Additional options supplied by the caller to be used in the LayerZero message.
bytes composeMsg; // The composed message for the send() operation.
bytes oftCmd; // The OFT command to be executed, unused in default OFT implementations.
}
struct MessagingFee {
uint256 nativeFee;
uint256 lzTokenFee;
}
// ========================================= L1StandardBridge =========================================
struct WithdrawalTransaction {
uint256 nonce;
address sender;
address target;
uint256 value;
uint256 gasLimit;
bytes data;
}
struct OutputRootProof {
bytes32 version;
bytes32 stateRoot;
bytes32 messagePasserStorageRoot;
bytes32 latestBlockhash;
}
// ========================================= Mantle L1StandardBridge =========================================
struct MantleWithdrawalTransaction {
uint256 nonce;
address sender;
address target;
uint256 mntValue;
uint256 value;
uint256 gasLimit;
bytes data;
}
// ========================================= Linea Bridge =========================================
struct ClaimMessageWithProofParams {
bytes32[] proof;
uint256 messageNumber;
uint32 leafIndex;
address from;
address to;
uint256 fee;
uint256 value;
address payable feeRecipient;
bytes32 merkleRoot;
bytes data;
}
// ========================================= Scroll Bridge =========================================
struct L2MessageProof {
uint256 batchIndex;
bytes merkleProof;
}
// ========================================= Camelot V3 / Algebra V3 =========================================
struct CamelotMintParams {
address token0;
address token1;
int24 tickLower;
int24 tickUpper;
uint256 amount0Desired;
uint256 amount1Desired;
uint256 amount0Min;
uint256 amount1Min;
address recipient;
uint256 deadline;
}
// ========================================= Algebra V4 =========================================
struct AlgebraMintParams {
address token0;
address token1;
address deployer;
int24 tickLower;
int24 tickUpper;
uint256 amount0Desired;
uint256 amount1Desired;
uint256 amount0Min;
uint256 amount1Min;
address recipient;
uint256 deadline;
}
// ========================================= Velodrome V3 =========================================
struct VelodromeMintParams {
address token0;
address token1;
int24 tickSpacing;
int24 tickLower;
int24 tickUpper;
uint256 amount0Desired;
uint256 amount1Desired;
uint256 amount0Min;
uint256 amount1Min;
address recipient;
uint256 deadline;
uint160 sqrtPriceX96;
}
// ========================================= Karak =========================================
struct QueuedWithdrawal {
address staker;
address delegatedTo;
uint256 nonce;
uint256 start;
WithdrawRequest request;
}
struct WithdrawRequest {
address[] vaults;
uint256[] shares;
address withdrawer;
}
// ========================================= Term Finance ==================================
/// @dev TermAuctionOfferSubmission represents an offer submission to offeror an amount of money for a specific interest rate
struct TermAuctionOfferSubmission {
/// @dev For an existing offer this is the unique onchain identifier for this offer. For a new offer this is a randomized input that will be used to generate the unique onchain identifier.
bytes32 id;
/// @dev The address of the offeror
address offeror;
/// @dev Hash of the offered price as a percentage of the initial loaned amount vs amount returned at maturity. This stores 9 decimal places
bytes32 offerPriceHash;
/// @dev The maximum amount of purchase tokens that can be lent
uint256 amount;
/// @dev The address of the ERC20 purchase token
address purchaseToken;
}
// ========================================= Dolomite Finance ==================================
enum BalanceCheckFlag {
Both,
From,
To,
None
}
// ========================================= Silo Finance ==================================
/// @dev There are 2 types of accounting in the system: for non-borrowable collateral deposit called "protected" and
/// for borrowable collateral deposit called "collateral". System does
/// identical calculations for each type of accounting but it uses different data. To avoid code duplication
/// this enum is used to decide which data should be read.
enum CollateralType {
Protected, // default
Collateral
}
enum ActionType {
Deposit,
Mint,
Repay,
RepayShares
}
struct Action {
// what do you want to do?
uint8 actionType;
// which Silo are you interacting with?
address silo;
// what asset do you want to use?
address asset;
// options specific for actions
bytes options;
}
struct AnyAction {
// how much assets or shares do you want to use?
uint256 amount;
// are you using Protected, Collateral
uint8 assetType;
}
// ========================================= LBTC Bridge ==================================
struct DepositBridgeAction {
uint256 fromChain;
bytes32 fromContract;
uint256 toChain;
address toContract;
address recipient;
uint64 amount;
uint256 nonce;
}
// ========================================= Odos ==================================
struct swapTokenInfo {
address inputToken;
uint256 inputAmount;
address inputReceiver;
address outputToken;
uint256 outputQuote;
uint256 outputMin;
address outputReceiver;
}
struct swapTokenInfoOogaBooga {
address inputToken;
uint256 inputAmount;
address outputToken;
uint256 outputQuote;
uint256 outputMin;
address outputReceiver;
}
// ========================================= Level ==================================
/// @dev for reference
//enum OrderType {
// MINT,
// REDEEM
//}
struct LevelOrder {
uint8 order_type;
address benefactor;
address beneficiary;
address collateral_asset;
uint256 collateral_amount;
uint256 lvlusd_amount;
}
struct LevelOrderV2 {
address beneficiary;
address collateral_asset;
uint256 collateral_amount;
uint256 min_lvlusd_amount;
}
struct Route {
address[] addresses;
uint256[] ratios;
}
// ========================================= Royco ==================================
struct APOffer { // RecipeMarketHub
uint256 offerID;
bytes32 targetMarketHash;
address ap;
address fundingVault;
uint256 quantity;
uint256 expiry;
address[] incentivesRequested;
uint256[] incentiveAmountsRequested;
}
struct APOfferVault { // VaultMarketHub (renamed to avoid collision)
uint256 offerID;
address targetVault;
address ap;
address fundingVault;
uint256 expiry;
address[] incentivesRequested;
uint256[] incentivesRatesRequested;
}
struct Reward {
uint48 startEpoch;
uint48 endEpoch;
address token;
uint256 rewardRate;
}
// ========================================= Permit2 ==================================
struct TokenSpenderPair {
address token;
address spender;
}
// ========================================= OnChainQueue ==================================
struct OnChainWithdraw {
uint96 nonce; // read from state, used to make it impossible for request Ids to be repeated.
address user; // msg.sender
address assetOut; // input sanitized
uint128 amountOfShares; // input transfered in
uint128 amountOfAssets; // derived from amountOfShares and price
uint40 creationTime; // time withdraw was made
uint24 secondsToMaturity; // in contract, from withdrawAsset?
uint24 secondsToDeadline; // in contract, from withdrawAsset? To get the deadline you take the creationTime add seconds to maturity, add the secondsToDeadline
}
// ========================================= Beraborrow ==================================
struct OpenDenVaultParams {
address denManager;
address collVault;
uint256 _maxFeePercentage;
uint256 _debtAmount;
uint256 _collAssetToDeposit;
address _upperHint;
address _lowerHint;
uint256 _minSharesMinted;
uint256 _collIndex;
bytes _preDeposit;
}
struct AdjustDenVaultParams {
address denManager;
address collVault;
uint256 _maxFeePercentage;
uint256 _collAssetToDeposit;
uint256 _collWithdrawal;
uint256 _debtChange;
bool _isDebtIncrease;
address _upperHint;
address _lowerHint;
bool unwrap;
uint256 _minSharesMinted;
uint256 _minAssetsWithdrawn;
uint256 _collIndex;
bytes _preDeposit;
}
struct RedeemCollateralVaultParams {
address denManager;
address collVault;
uint256 _debtAmount;
address _firstRedemptionHint;
address _upperPartialRedemptionHint;
address _lowerPartialRedemptionHint;
uint256 _partialRedemptionHintNICR;
uint256 _maxIterations;
uint256 _maxFeePercentage;
uint256 _minSharesWithdrawn;
uint256 minAssetsWithdrawn;
uint256 collIndex;
bool unwrap;
}
struct AddCollParams {
address upperHint;
address lowerHint;
uint256 minSharesOut;
uint256 minCollVaultShares;
}
struct ExternalRebalanceParams {
address swapper;
bytes payload;
uint256 minRebalanceOut;
}
}
"
},
"src/interfaces/BalancerVault.sol": {
"content": "// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.21;
import {DecoderCustomTypes} from "src/interfaces/DecoderCustomTypes.sol";
interface BalancerVault {
function flashLoan(address, address[] memory tokens, uint256[] memory amounts, bytes calldata userData) external;
function swap(
DecoderCustomTypes.SingleSwap memory singleSwap,
DecoderCustomTypes.FundManagement memory funds,
uint256 limit,
uint256 deadline
) external returns (uint256 amountCalculated);
}
"
},
"src/base/BoringVault.sol": {
"content": "// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.21;
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {ERC721Holder} from "@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol";
import {ERC1155Holder} from "@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol";
import {FixedPointMathLib} from "@solmate/utils/FixedPointMathLib.sol";
import {SafeTransferLib} from "@solmate/utils/SafeTransferLib.sol";
import {ERC20} from "@solmate/tokens/ERC20.sol";
import {BeforeTransferHook} from "src/interfaces/BeforeTransferHook.sol";
import {Auth, Authority} from "@solmate/auth/Auth.sol";
contract BoringVault is ERC20, Auth, ERC721Holder, ERC1155Holder {
using Address for address;
using SafeTransferLib for ERC20;
using FixedPointMathLib for uint256;
// ========================================= STATE =========================================
/**
* @notice Contract responsbile for implementing `beforeTransfer`.
*/
BeforeTransferHook public hook;
//============================== EVENTS ===============================
event Enter(address indexed from, address indexed asset, uint256 amount, address indexed to, uint256 shares);
event Exit(address indexed to, address indexed asset, uint256 amount, address indexed from, uint256 shares);
//============================== CONSTRUCTOR ===============================
constructor(address _owner, string memory _name, string memory _symbol, uint8 _decimals)
ERC20(_name, _symbol, _decimals)
Auth(_owner, Authority(address(0)))
{}
//============================== MANAGE ===============================
/**
* @notice Allows manager to make an arbitrary function call from this contract.
* @dev Callable by MANAGER_ROLE.
*/
function manage(address target, bytes calldata data, uint256 value)
external
requiresAuth
returns (bytes memory result)
{
result = target.functionCallWithValue(data, value);
}
/**
* @notice Allows manager to make arbitrary function calls from this contract.
* @dev Callable by MANAGER_ROLE.
*/
function manage(address[] calldata targets, bytes[] calldata data, uint256[] calldata values)
external
requiresAuth
returns (bytes[] memory results)
{
uint256 targetsLength = targets.length;
results = new bytes[](targetsLength);
for (uint256 i; i < targetsLength; ++i) {
results[i] = targets[i].functionCallWithValue(data[i], values[i]);
}
}
//============================== ENTER ===============================
/**
* @notice Allows minter to mint shares, in exchange for assets.
* @dev If assetAmount is zero, no assets are transferred in.
* @dev Callable by MINTER_ROLE.
*/
function enter(address from, ERC20 asset, uint256 assetAmount, address to, uint256 shareAmount)
external
requiresAuth
{
// Transfer assets in
if (assetAmount > 0) asset.safeTransferFrom(from, address(this), assetAmount);
// Mint shares.
_mint(to, shareAmount);
emit Enter(from, address(asset), assetAmount, to, shareAmount);
}
//============================== EXIT ===============================
/**
* @notice Allows burner to burn shares, in exchange for assets.
* @dev If assetAmount is zero, no assets are transferred out.
* @dev Callable by BURNER_ROLE.
*/
function exit(address to, ERC20 asset, uint256 assetAmount, address from, uint256 shareAmount)
external
requiresAuth
{
// Burn shares.
_burn(from, shareAmount);
// Transfer assets out.
if (assetAmount > 0) asset.safeTransfer(to, assetAmount);
emit Exit(to, address(asset), assetAmount, from, shareAmount);
}
//============================== BEFORE TRANSFER HOOK ===============================
/**
* @notice Sets the share locker.
* @notice If set to zero address, the share locker logic is disabled.
* @dev Callable by OWNER_ROLE.
*/
function setBeforeTransferHook(address _hook) external requiresAuth {
hook = BeforeTransferHook(_hook);
}
/**
* @notice Call `beforeTransferHook` passing in `from` `to`, and `msg.sender`.
*/
function _callBeforeTransfer(address from, address to) internal view {
if (address(hook) != address(0)) hook.beforeTransfer(from, to, msg.sender);
}
function transfer(address to, uint256 amount) public override returns (bool) {
_callBeforeTransfer(msg.sender, to);
return super.transfer(to, amount);
}
function transferFrom(address from, address to, uint256 amount) public override returns (bool) {
_callBeforeTransfer(from, to);
return super.transferFrom(from, to, amount);
}
//============================== RECEIVE ===============================
receive() external payable {}
}
"
},
"lib/solmate/src/auth/Auth.sol": {
"content": "// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Provides a flexible and updatable auth pattern which is completely separate from application logic.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Auth.sol)
/// @author Modified from Dappsys (https://github.com/dapphub/ds-auth/blob/master/src/auth.sol)
abstract contract Auth {
event OwnershipTransferred(address indexed user, address indexed newOwner);
event AuthorityUpdated(address indexed user, Authority indexed newAuthority);
address public owner;
Authority public authority;
constructor(address _owner, Authority _authority) {
owner = _owner;
authority = _authority;
emit OwnershipTransferred(msg.sender, _owner);
emit AuthorityUpdated(msg.sender, _authority);
}
modifier requiresAuth() virtual {
require(isAuthorized(msg.sender, msg.sig), "UNAUTHORIZED");
_;
}
function isAuthorized(address user, bytes4 functionSig) internal view virtual returns (bool) {
Authority auth = authority; // Memoizing authority saves us a warm SLOAD, around 100 gas.
// Checking if the caller is the owner only after calling the authority saves gas in most cases, but be
// aware that this makes protected functions uncallable even to the owner if the authority is out of order.
return (address(auth) != address(0) && auth.canCall(user, address(this), functionSig)) || user == owner;
}
function setAuthority(Authority newAuthority) public virtual {
// We check if the caller is the owner first because we want to ensure they can
// always swap out the authority even if it's reverting or using up a lot of gas.
require(msg.sender == owner || authority.canCall(msg.sender, address(this), msg.sig));
authority = newAuthority;
emit AuthorityUpdated(msg.sender, newAuthority);
}
function transferOwnership(address newOwner) public virtual requiresAuth {
owner = newOwner;
emit OwnershipTransferred(msg.sender, newOwner);
}
}
/// @notice A generic interface for a contract which provides authorization data to an Auth instance.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Auth.sol)
/// @author Modified from Dappsys (https://github.com/dapphub/ds-auth/blob/master/src/auth.sol)
interface Authority {
function canCall(
address user,
address target,
bytes4 functionSig
) external view returns (bool);
}
"
},
"lib/solmate/src/utils/FixedPointMathLib.sol": {
"content": "// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=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)
library FixedPointMathLib {
/*//////////////////////////////////////////////////////////////
SIMPLIFIED FIXED POINT OPERATIONS
//////////////////////////////////////////////////////////////*/
uint256 internal constant MAX_UINT256 = 2**256 - 1;
uint256 internal constant WAD = 1e18; // The scalar of ETH and most ERC20s.
function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down.
}
function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivUp(x, y, WAD); // Equivalent to (x * y) / WAD rounded up.
}
function divWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivDown(x, WAD, y); // Equivalent to (x * WAD) / y rounded down.
}
function divWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up.
}
/*//////////////////////////////////////////////////////////////
LOW LEVEL FIXED POINT OPERATIONS
//////////////////////////////////////////////////////////////*/
function mulDivDown(
uint256 x,
uint256 y,
uint256 denominator
) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
if iszero(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)
}
}
function mulDivUp(
uint256 x,
uint256 y,
uint256 denominator
) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
if iszero(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))
}
}
function rpow(
uint256 x,
uint256 n,
uint256 scalar
) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
switch x
case 0 {
switch n
case 0 {
// 0 ** 0 = 1
z := scalar
}
default {
// 0 ** n = 0
z := 0
}
}
default {
switch mod(n, 2)
case 0 {
// 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.
if shr(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.
if lt(xxRound, xx) {
revert(0, 0)
}
// Set x to scaled xxRound.
x := div(xxRound, scalar)
// If n is even:
if mod(n, 2) {
// Compute z * x.
let zx := mul(z, x)
// If z * x overflowed:
if iszero(eq(div(zx, x), z)) {
// Revert if x is non-zero.
if iszero(iszero(x)) {
revert(0, 0)
}
}
// Round to the nearest number.
let zxRound := add(zx, half)
// Revert if zx + half overflowed.
if lt(zxRound, zx) {
revert(0, 0)
}
// Return properly scaled zxRound.
z := div(zxRound, scalar)
}
}
}
}
}
/*//////////////////////////////////////////////////////////////
GENERAL NUMBER UTILITIES
//////////////////////////////////////////////////////////////*/
function sqrt(uint256 x) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
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.
if iszero(lt(y, 0x10000000000000000000000000000000000)) {
y := shr(128, y)
z := shl(64, z)
}
if iszero(lt(y, 0x1000000000000000000)) {
y := shr(64, y)
z := shl(32, z)
}
if iszero(lt(y, 0x10000000000)) {
y := shr(32, y)
z := shl(16, z)
}
if iszero(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))
}
}
function unsafeMod(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Mod x by y. Note this will return
// 0 instead of reverting if y is zero.
z := mod(x, y)
}
}
function unsafeDiv(uint256 x, uint256 y) internal pure returns (uint256 r) {
/// @solidity memory-safe-assembly
assembly {
// Divide x by y. Note this will return
// 0 instead of reverting if y is zero.
r := div(x, y)
}
}
function unsafeDivUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// 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))
}
}
}
"
},
"src/base/Roles/ManagerWithMerkleVerification.sol": {
"content": "// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.21;
import {FixedPointMathLib} from "@solmate/utils/FixedPointMathLib.sol";
import {BoringVault} from "src/base/BoringVault.sol";
import {MerkleProofLib} from "@solmate/utils/MerkleProofLib.sol";
import {ERC20} from "@solmate/tokens/ERC20.sol";
import {SafeTransferLib} from "@solmate/utils/SafeTransferLib.sol";
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {BalancerVault} from "src/interfaces/BalancerVault.sol";
import {Auth, Authority} from "@solmate/auth/Auth.sol";
import {IPausable} from "src/interfaces/IPausable.sol";
import {DroneLib} from "src/base/Drones/DroneLib.sol";
contract ManagerWithMerkleVerification is Auth, IPausable {
using FixedPointMathLib for uint256;
using SafeTransferLib for ERC20;
using Address for address;
// ========================================= STATE =========================================
/**
* @notice A merkle tree root that restricts what data can be passed to the BoringVault.
* @dev Maps a strategist address to their specific merkle root.
* @dev Each leaf is composed of the keccak256 hash of abi.encodePacked {decodersAndSanitizer, target, valueIsNonZero, selector, argumentAddress_0, ...., argumentAddress_N}
* Where:
* - decodersAndSanitizer is the addres to call to extract packed address arguments from the calldata
* - target is the address to make the call to
* - valueIsNonZero is a bool indicating whether or not the value is non-zero
* - selector is the function selector on target
* - argumentAddress is each allowed address argument in that call
*/
mapping(address => bytes32) public manageRoot;
/**
* @notice Bool indicating whether or not this contract is actively performing a flash loan.
* @dev Used to block flash loans that are initiated outside a manage call.
*/
bool internal performingFlashLoan;
/**
* @notice keccak256 hash of flash loan data.
*/
bytes32 internal flashLoanIntentHash = bytes32(0);
/**
* @notice Used to pause calls to `manageVaultWithMerkleVerification`.
*/
bool public isPaused;
//============================== ERRORS ===============================
error ManagerWithMerkleVerification__InvalidManageProofLength();
error ManagerWithMerkleVerification__InvalidTargetDataLength();
error ManagerWithMerkleVerification__InvalidValuesLength();
error ManagerWithMerkleVerification__InvalidDecodersAndSanitizersLength();
error ManagerWithMerkleVerification__FlashLoanNotExecuted();
error ManagerWithMerkleVerification__FlashLoanNotInProgress();
error ManagerWithMerkleVerification__BadFlashLoanIntentHash();
error ManagerWithMerkleVerification__FailedToVerifyManageProof(address target, bytes targetData, uint256 value);
error ManagerWithMerkleVerification__Paused();
error ManagerWithMerkleVerification__OnlyCallableByBoringVault();
error ManagerWithMerkleVerification__OnlyCallableByBalancerVault();
error ManagerWithMerkleVerification__TotalSupplyMustRemainConstantDuringPlatform();
//============================== EVENTS ===============================
event ManageRootUpdated(address indexed strategist, bytes32 oldRoot, bytes32 newRoot);
event BoringVaultManaged(uint256 callsMade);
event Paused();
event Unpaused();
//============================== IMMUTABLES ===============================
/**
* @notice The BoringVault this contract can manage.
*/
BoringVault public immutable vault;
/**
* @notice The balancer vault this contract can use for flash loans.
*/
BalancerVault public immutable balancerVault;
constructor(address _owner, address _vault, address _balancerVault) Auth(_owner, Authority(address(0))) {
vault = BoringVault(payable(_vault));
balancerVault = BalancerVault(_balancerVault);
}
// ========================================= ADMIN FUNCTIONS =========================================
/**
* @notice Sets the manageRoot.
* @dev Callable by OWNER_ROLE.
*/
function setManageRoot(address strategist, bytes32 _manageRoot) external requiresAuth {
bytes32 oldRoot = manageRoot[strategist];
manageRoot[strategist] = _manageRoot;
emit ManageRootUpdated(strategist, oldRoot, _manageRoot);
}
/**
* @notice Pause this contract, which prevents future calls to `manageVaultWithMerkleVerification`.
* @dev Callable by MULTISIG_ROLE.
*/
function pause() external requiresAuth {
isPaused = true;
emit Paused();
}
/**
* @notice Unpause this contract, which allows future calls to `manageVaultWithMerkleVerification`.
* @dev Callable by MULTISIG_ROLE.
*/
function unpause() external requiresAuth {
isPaused = false;
emit Unpaused();
}
// ========================================= STRATEGIST F
Submitted on: 2025-10-23 18:34:38
Comments
Log in to comment.
No comments yet.