// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
*
* Furthermore, `isContract` will also return true if the target contract within
* the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
* which only has an effect at the end of a transaction.
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
// only check isContract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface AggregatorInterface {
function latestAnswer() external view returns (int256);
function latestTimestamp() external view returns (uint256);
function latestRound() external view returns (uint256);
function getAnswer(uint256 roundId) external view returns (int256);
function getTimestamp(uint256 roundId) external view returns (uint256);
event AnswerUpdated(int256 indexed current, uint256 indexed roundId, uint256 updatedAt);
event NewRound(uint256 indexed roundId, address indexed startedBy, uint256 startedAt);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./AggregatorInterface.sol";
import "./AggregatorV3Interface.sol";
interface AggregatorV2V3Interface is AggregatorInterface, AggregatorV3Interface {}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface AggregatorV3Interface {
function decimals() external view returns (uint8);
function description() external view returns (string memory);
function version() external view returns (uint256);
function getRoundData(uint80 _roundId)
external
view
returns (
uint80 roundId,
int256 answer,
uint256 startedAt,
uint256 updatedAt,
uint80 answeredInRound
);
function latestRoundData()
external
view
returns (
uint80 roundId,
int256 answer,
uint256 startedAt,
uint256 updatedAt,
uint80 answeredInRound
);
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.17;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {TransferHelper} from "@uniswap/lib/contracts/libraries/TransferHelper.sol";
import {IAllowanceTransfer} from "../core/interfaces/IAllowanceTransfer.sol";
import {ErrorLibrary} from "../library/ErrorLibrary.sol";
import {IPortfolio} from "../core/interfaces/IPortfolio.sol";
import {FunctionParameters} from "../FunctionParameters.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
/**
* @title DepositBatch
* @notice A contract for performing multi-token swap and deposit operations.
* @dev This contract uses Enso's swap execution logic for delegating swaps.
*/
contract DepositBatch is ReentrancyGuard {
// The address of Enso's swap execution logic; swaps are delegated to this target.
address constant SWAP_TARGET = 0x38147794FF247e5Fc179eDbAE6C37fff88f68C52;
/**
* @notice Performs a multi-token swap and deposit operation for the user.
* @param data Struct containing parameters for the batch handler.
*/
function multiTokenSwapETHAndTransfer(
FunctionParameters.BatchHandler memory data
) external payable nonReentrant {
if (msg.value == 0) {
revert ErrorLibrary.InvalidBalance();
}
address user = msg.sender;
_multiTokenSwapAndDeposit(data, user);
(bool sent, ) = user.call{value: address(this).balance}("");
if (!sent) revert ErrorLibrary.TransferFailed();
}
/**
* @notice Performs a multi-token swap and deposit operation for the user.
* @param data Struct containing parameters for the batch handler.
*/
function multiTokenSwapAndDeposit(
FunctionParameters.BatchHandler memory data,
address user
) external payable nonReentrant {
address _depositToken = data._depositToken;
_multiTokenSwapAndDeposit(data, user);
// Return any leftover invested token dust to the user
uint256 depositTokenBalance = _getTokenBalance(
_depositToken,
address(this)
);
if (depositTokenBalance > 0) {
TransferHelper.safeTransfer(_depositToken, user, depositTokenBalance);
}
}
function _multiTokenSwapAndDeposit(
FunctionParameters.BatchHandler memory data,
address user
) internal {
address[] memory tokens = IPortfolio(data._target).getTokens();
address _depositToken = data._depositToken;
address target = data._target;
uint256 tokenLength = tokens.length;
uint256[] memory depositAmounts = new uint256[](tokenLength);
if (data._callData.length != tokenLength)
revert ErrorLibrary.InvalidLength();
// Perform swaps and calculate deposit amounts for each token
for (uint256 i; i < tokenLength; i++) {
address _token = tokens[i];
uint256 balance;
if (_token == _depositToken) {
//Sending encoded balance instead of swap calldata
balance = abi.decode(data._callData[i], (uint256));
} else {
uint256 balanceBefore = _getTokenBalance(_token, address(this));
(bool success, ) = SWAP_TARGET.delegatecall(data._callData[i]);
if (!success) revert ErrorLibrary.CallFailed();
uint256 balanceAfter = _getTokenBalance(_token, address(this));
balance = balanceAfter - balanceBefore;
}
if (balance == 0) revert ErrorLibrary.InvalidBalanceDiff();
IERC20(_token).approve(target, 0);
IERC20(_token).approve(target, balance);
depositAmounts[i] = balance;
}
IPortfolio(target).multiTokenDepositFor(
user,
depositAmounts,
data._minMintAmount
);
//Return any leftover vault token dust to the user
for (uint256 i; i < tokenLength; i++) {
address _token = tokens[i];
uint256 portfoliodustReturn = _getTokenBalance(_token, address(this));
if (portfoliodustReturn > 0) {
TransferHelper.safeTransfer(_token, user, portfoliodustReturn);
}
}
}
/**
* @notice Helper function to get balance of any token for any user.
* @param _token Address of token to get balance.
* @param _of Address of user to get balance of.
* @return uint256 Balance of the specified token for the user.
*/
function _getTokenBalance(
address _token,
address _of
) internal view returns (uint256) {
return IERC20(_token).balanceOf(_of);
}
// Function to receive Ether when msg.data is empty
receive() external payable {}
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.17;
/**
* @title ErrorLibrary
* @author Velvet.Capital
* @notice This is a library contract including custom defined errors
*/
library ErrorLibrary {
/// @notice Thrown when caller is not rebalancer contract
error CallerNotRebalancerContract();
/// @notice Thrown when caller is not asset manager
error CallerNotAssetManager();
/// @notice Thrown when caller is not asset manager
error CallerNotSuperAdmin();
/// @notice Thrown when caller is not whitelist manager
error CallerNotWhitelistManager();
/// @notice Thrown when length of tokens array is zero
error InvalidLength();
/// @notice Thrown when user is not allowed to deposit
error UserNotAllowedToDeposit();
/// @notice Thrown when portfolio token in not initialized
error PortfolioTokenNotInitialized();
/// @notice Thrown when caller is not holding enough portfolio token amount to withdraw
error CallerNotHavingGivenPortfolioTokenAmount();
/// @notice Thrown when the tokens are already initialized
error AlreadyInitialized();
/// @notice Thrown when the token is not whitelisted
error TokenNotWhitelisted();
/// @notice Thrown when token address being passed is zero
error InvalidTokenAddress();
/// @notice Thrown when transfer is prohibited
error Transferprohibited();
/// @notice Thrown when caller is not portfolio manager
error CallerNotPortfolioManager();
/// @notice Thrown when offchain handler is not valid
error InvalidSolver();
/// @notice Thrown when set time period is not over
error TimePeriodNotOver();
/// @notice Thrown when trying to set any fee greater than max allowed fee
error InvalidFee();
/// @notice Thrown when zero address is passed for treasury
error ZeroAddressTreasury();
/// @notice Thrown when previous address is passed for treasury
error PreviousTreasuryAddress();
/// @notice Thrown when zero address is being passed
error InvalidAddress();
/// @notice Thrown when caller is not the owner
error CallerNotOwner();
/// @notice Thrown when protocol is not paused
error ProtocolNotPaused();
/// @notice Thrown when protocol is paused
error ProtocolIsPaused();
/// @notice Thrown when token is not enabled
error TokenNotEnabled();
/// @notice Thrown when portfolio creation is paused
error PortfolioCreationIsPause();
/// @notice Thrown when asset manager is trying to input token which already exist
error TokenAlreadyExist();
/// @notice Thrown when cool down period is not passed
error CoolDownPeriodNotPassed();
/// @notice Throws when the setup is failed in gnosis
error ModuleNotInitialised();
/// @notice Throws when threshold is more than owner length
error InvalidThresholdLength();
/// @notice Throws when no owner address is passed while fund creation
error NoOwnerPassed();
/// @notice Thorws when the caller does not have a default admin role
error CallerNotAdmin();
/// @notice Throws when a public fund is tried to made transferable only to whitelisted addresses
error PublicFundToWhitelistedNotAllowed();
/// @notice Generic call failed error
error CallFailed();
/// @notice Generic transfer failed error
error TransferFailed();
/// @notice Throws when the initToken or updateTokenList function of Portfolio is having more tokens than set by the Registry
error TokenCountOutOfLimit(uint256 limit);
/// @notice Throws when the array lenghts don't match for adding price feed or enabling tokens
error IncorrectArrayLength();
/// @notice Throws when user calls updateFees function before proposing a new fee
error NoNewFeeSet();
/// @notice Throws when sequencer is down
error SequencerIsDown();
/// @notice Throws when sequencer threshold is not crossed
error SequencerThresholdNotCrossed();
/// @notice Throws when depositAmount and depositToken length does not match
error InvalidDepositInputLength();
/// @notice Mint amount smaller than users indended buy amount
error InvalidMintAmount();
/// @notice Thorws when zero price is set for min portfolio price
error InvalidMinPortfolioAmount();
/// @notice Thorws when min portfolio price is set less then min portfolio price set by protocol
error InvalidMinPortfolioAmountByAssetManager();
/// @notice Throws when assetManager set zero or less initial portfolio price then set by protocol
error InvalidInitialPortfolioAmount();
/// @notice Throws when zero amount or amount less then protocol minPortfolioAmount is set while updating min Portfolio amount by assetManager
error InvalidMinPortfolioTokenHoldingAmount();
/// @notice Throws when assetmanager set min portfolio amount less then acceptable amount set by protocol
error InvalidMinAmountByAssetManager();
/// @notice Throws when user is not maintaining min portfolio token amount while withdrawing
error CallerNeedToMaintainMinTokenAmount();
/// @notice Throws when user minted amount during deposit is less then set by assetManager
error MintedAmountIsNotAccepted();
/// @notice Throws when balance of buyToken after rebalance is zero
error BalanceOfVaultCannotNotBeZero(address);
/// @notice Throws when balance of selltoken in handler after swap is not zero
error BalanceOfHandlerShouldBeZero();
/// @notice Throws when balance of selltoken in handler after swap is exceeding dust
error BalanceOfHandlerShouldNotExceedDust();
/// @notice Throws when swap return value in handler is less then min buy amounts
error ReturnValueLessThenExpected();
/// @notice Throws when non portfolio token balance in not zero after rebalance
error NonPortfolioTokenBalanceIsNotZero();
/// @notice Throws when the oracle price is not updated under set timeout
error PriceOracleExpired();
/// @notice Throws when the oracle price is returned 0
error PriceOracleInvalid();
/// @notice Thrown when oracle address is zero address
error InvalidOracleAddress();
/// @notice Thrown when token is not in price oracle
error TokenNotInPriceOracle();
/// @notice Throws when token is not removed and user is trying to claim
error NoTokensRemoved();
/// @notice Throws when assetManager tries to remove portfolioToken
error IsPortfolioToken();
/// @notice Throws when disabled tokens are used in protocol
error NotPortfolioToken();
/// @notice Thrown when balance of vault is zero
error BalanceOfVaultIsZero();
/// @notice Thrown when max asset limit is set zero
error InvalidAssetLimit();
/// @notice Thrown when max whitelist limit is set zero
error InvalidWhitelistLimit();
/// @notice Thrown when withdrawal amount is too small and tokenBalance in return is zero
error WithdrawalAmountIsSmall();
/// @notice Thrown when deposit amount is zero
error AmountCannotBeZero();
// @notice Thrown when percentage of token to remove is invalid
error InvalidTokenRemovalPercentage();
// @notice Thrown when user passes the wrong buy token list (not equal to buy tokens in calldata)
error InvalidBuyTokenList();
/// @notice Thrown when permitted to wrong spender
error InvalidSpender();
/// @notice Thrown when claiming reward tokens failed
error ClaimFailed();
/// @notice Thrown when protocol owner passed invalid protocol streaming fee
error InvalidProtocolStreamingFee();
/// @notice Thrown when protocol owner passed invalid protocol fee
error InvalidProtocolFee();
/// @notice Thrown when protocol is emergency paused
error ProtocolEmergencyPaused();
/// @notice Thrown when batchHandler balance diff is zero
error InvalidBalanceDiff();
// @notice Thrown when an unpause action is attempted too soon after the last unpause.
error TimeSinceLastUnpauseNotElapsed();
// @notice Thrown when an invalid cooldown period is set.
error InvalidCooldownPeriod();
// @notice Thrown when the division by zero occurs
error DivisionByZero();
// @notice Thrown when the token whitelist length is zero
error InvalidTokenWhitelistLength();
// @notice Thrown when the reward target is not enabled
error RewardTargetNotEnabled();
// @notice Thrown when the allowance is insufficient
error InsufficientAllowance();
// @notice Thrown when user tries to claim for invalid Id
error InvalidId();
// @notice Thrown when exemption does match token to withdraw
error InvalidExemptionTokens();
// @notice Thrown when exemption tokens length is greater then portfolio tokens length
error InvalidExemptionTokensLength();
// @notice Thrown when the dust tolerance input is invalid
error InvalidDustTolerance();
// @notice Thrown when the target address is not whitelisted
error InvalidTargetAddress();
// @notice Thrown when the ETH balance sent is zero
error InvalidBalance();
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.17;
/**
* @title FunctionParameters
* @notice A library for defining structured data passed across functions in DeFi protocols.
* @dev This library encapsulates various structures used for initializing, configuring, and managing on-chain financial products.
*/
library FunctionParameters {
/**
* @notice Struct for initializing a new PortfolioFactory
* @dev Encapsulates data necessary for deploying an PortfolioFactory and associated components.
* @param _basePortfolioAddress Base Portfolio contract address for cloning
* @param _baseTokenExclusionManagerAddress Base Token Exclusion address for cloning
* @param _baseRebalancingAddress Base Rebalancing module address for cloning
* @param _baseAssetManagementConfigAddress Base AssetManagement Config address for cloning
* @param _feeModuleImplementationAddress Fee Module implementation contract address
* @param _baseTokenRemovalVaultImplementation Token Removal Vault implementation contract address
* @param _baseVelvetGnosisSafeModuleAddress Base Gnosis-Safe module address for cloning
* @param _gnosisSingleton Gnosis Singleton contract address
* @param _gnosisFallbackLibrary Gnosis Fallback Library address
* @param _gnosisMultisendLibrary Gnosis Multisend Library address
* @param _gnosisSafeProxyFactory Gnosis Safe Proxy Factory address
* @param _protocolConfig Protocol configuration contract address
* @param _velvetProtocolFee Protocol fee percentage (in basis points)
*/
struct PortfolioFactoryInitData {
address _basePortfolioAddress;
address _baseTokenExclusionManagerAddress;
address _baseRebalancingAddres;
address _baseAssetManagementConfigAddress;
address _feeModuleImplementationAddress;
address _baseTokenRemovalVaultImplementation;
address _baseVelvetGnosisSafeModuleAddress;
address _gnosisSingleton;
address _gnosisFallbackLibrary;
address _gnosisMultisendLibrary;
address _gnosisSafeProxyFactory;
address _protocolConfig;
}
/**
* @notice Data for initializing the Portfolio module
* @dev Used when setting up a new Portfolio instance.
* @param _name Name of the Portfolio Fund
* @param _symbol Symbol of the Portfolio Fund
* @param _vault Vault address associated with the Portfolio Fund
* @param _module Safe module address associated with the Portfolio Fund
* @param _accessController Access Controller address for managing roles
* @param _protocolConfig Protocol configuration contract address
* @param _assetManagementConfig Asset Management configuration contract address
* @param _feeModule Fee Module contract address
*/
struct PortfolioInitData {
string _name;
string _symbol;
address _vault;
address _module;
address _tokenExclusionManager;
address _accessController;
address _protocolConfig;
address _assetManagementConfig;
address _feeModule;
}
/**
* @notice Data for initializing a new Portfolio Fund via the Factory
* @dev Encapsulates settings and configurations for a newly created Portfolio Fund.
* @param _assetManagerTreasury Treasury address for asset manager fee accumulation
* @param _whitelistedTokens Array of token addresses permitted in the Portfolio Fund
* @param _managementFee Management fee (annual, in basis points)
* @param _performanceFee Performance fee (upon profit, in basis points)
* @param _entryFee Fee for entering the fund (in basis points)
* @param _exitFee Fee for exiting the fund (in basis points)
* @param _initialPortfolioAmount Initial amount of the portfolio token
* @param _minPortfolioTokenHoldingAmount Minimum amount of portfolio tokens that can be held and can be minted
* @param _public Indicates if the fund is open to the public
* @param _transferable Indicates if the fund's tokens are transferable
* @param _transferableToPublic Indicates if the fund's tokens are transferable to the public
* @param _whitelistTokens Indicates if only whitelisted tokens can be included in the fund
* @param _name Name of the Portfolio Fund
* @param _symbol Symbol of the Portfolio Fund
*/
struct PortfolioCreationInitData {
address _assetManagerTreasury;
address[] _whitelistedTokens;
uint256 _managementFee;
uint256 _performanceFee;
uint256 _entryFee;
uint256 _exitFee;
uint256 _initialPortfolioAmount;
uint256 _minPortfolioTokenHoldingAmount;
bool _public;
bool _transferable;
bool _transferableToPublic;
bool _whitelistTokens;
string _name;
string _symbol;
}
/**
* @notice Data for initializing the Asset Manager Config
* @dev Used for setting up asset management configurations for an Portfolio Fund.
* @param _managementFee Annual management fee (in basis points)
* @param _performanceFee Performance fee (upon profit, in basis points)
* @param _entryFee Entry fee (in basis points)
* @param _exitFee Exit fee (in basis points)
* @param _initialPortfolioAmount Initial amount of the portfolio token
* @param _minPortfolioTokenHoldingAmount Minimum amount of portfolio tokens that can be held and can be minted
* @param _protocolConfig Protocol configuration contract address
* @param _accessController Access Controller contract address
* @param _assetManagerTreasury Treasury address for asset manager fee accumulation
* @param _whitelistedTokens Array of token addresses permitted in the Portfolio Fund
* @param _publicPortfolio Indicates if the portfolio is open to public deposits
* @param _transferable Indicates if the portfolio's tokens are transferable
* @param _transferableToPublic Indicates if the portfolio's tokens are transferable to the public
* @param _whitelistTokens Indicates if only whitelisted tokens can be included in the portfolio
*/
struct AssetManagementConfigInitData {
uint256 _managementFee;
uint256 _performanceFee;
uint256 _entryFee;
uint256 _exitFee;
uint256 _initialPortfolioAmount;
uint256 _minPortfolioTokenHoldingAmount;
address _protocolConfig;
address _accessController;
address _feeModule;
address _assetManagerTreasury;
address[] _whitelistedTokens;
bool _publicPortfolio;
bool _transferable;
bool _transferableToPublic;
bool _whitelistTokens;
}
/**
* @notice Data structure for setting up roles during Portfolio Fund creation
* @dev Used for assigning roles to various components of the Portfolio Fund ecosystem.
* @param _portfolio Portfolio contract address
* @param _protocolConfig Protocol configuration contract address
* @param _portfolioCreator Address of the portfolio creator
* @param _rebalancing Rebalancing module contract address
* @param _feeModule Fee Module contract address
*/
struct AccessSetup {
address _portfolio;
address _portfolioCreator;
address _rebalancing;
address _feeModule;
}
/**
* @notice Struct for defining a rebalance intent
* @dev Encapsulates the intent data for performing a rebalance operation.
* @param _newTokens Array of new token addresses to be included in the Portfolio Fund
* @param _sellTokens Array of token addresses to be sold during the rebalance
* @param _sellAmounts Corresponding amounts of each token to sell
* @param _handler Address of the intent handler for executing rebalance
* @param _callData Encoded call data for the rebalance operation
*/
struct RebalanceIntent {
address[] _newTokens;
address[] _sellTokens;
uint256[] _sellAmounts;
address _handler;
bytes _callData;
}
/**
* @notice Struct of batchHandler data
* @dev Encapsulates the data needed to batch transaction.
* @param _minMintAmount The minimum amount of portfolio tokens the user expects to receive for their deposit, protecting against slippage
* @param _depositAmount Amount to token to swap to vailt tokens
* @param _target Adress of portfolio contract to deposit
* @param _depositToken Address of token that needed to be swapped
* @param _callData Encoded call data for swap operation
*/
struct BatchHandler {
uint256 _minMintAmount;
uint256 _depositAmount;
address _target;
address _depositToken;
bytes[] _callData;
}
/**
* @dev Struct to encapsulate the parameters required for deploying a Safe and its associated modules.
* @param _gnosisSingleton Address of the Safe singleton contract.
* @param _gnosisSafeProxyFactory Address of the Safe Proxy Factory contract.
* @param _gnosisMultisendLibrary Address of the Multisend library contract.
* @param _gnosisFallbackLibrary Address of the Fallback library contract.
* @param _baseGnosisModule Address of the base module to be used.
* @param _owners Array of addresses to be designated as owners of the Safe.
* @param _threshold Number of owner signatures required to execute a transaction in the Safe.
*/
struct SafeAndModuleDeploymentParams {
address _gnosisSingleton;
address _gnosisSafeProxyFactory;
address _gnosisMultisendLibrary;
address _gnosisFallbackLibrary;
address _baseGnosisModule;
address[] _owners;
uint256 _threshold;
}
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.17;
import {IEIP712} from "./IEIP712.sol";
/// @title AllowanceTransfer
/// @notice Handles ERC20 token permissions through signature based allowance setting and ERC20 token transfers by checking allowed amounts
/// @dev Requires user's token approval on the Permit2 contract
interface IAllowanceTransfer is IEIP712 {
/// @notice Emits an event when the owner successfully invalidates an ordered nonce.
event NonceInvalidation(
address indexed owner,
address indexed token,
address indexed spender,
uint48 newNonce,
uint48 oldNonce
);
/// @notice Emits an event when the owner successfully sets permissions on a token for the spender.
event Approval(
address indexed owner,
address indexed token,
address indexed spender,
uint160 amount,
uint48 expiration
);
/// @notice Emits an event when the owner successfully sets permissions using a permit signature on a token for the spender.
event Permit(
address indexed owner,
address indexed token,
address indexed spender,
uint160 amount,
uint48 expiration,
uint48 nonce
);
/// @notice Emits an event when the owner sets the allowance back to 0 with the lockdown function.
event Lockdown(
address indexed owner,
address indexed token,
address indexed spender
);
/// @notice The permit data for a token
struct PermitDetails {
// ERC20 token address
address token;
// the maximum amount allowed to spend
uint160 amount;
// timestamp at which a spender's token allowances become invalid
uint48 expiration;
// an incrementing value indexed per owner,token,and spender for each signature
uint48 nonce;
}
/// @notice The permit message signed for a single token allowance
struct PermitSingle {
// the permit data for a single token alownce
PermitDetails details;
// address permissioned on the allowed tokens
address spender;
// deadline on the permit signature
uint256 sigDeadline;
}
/// @notice The permit message signed for multiple token allowances
struct PermitBatch {
// the permit data for multiple token allowances
PermitDetails[] details;
// address permissioned on the allowed tokens
address spender;
// deadline on the permit signature
uint256 sigDeadline;
}
/// @notice The saved permissions
/// @dev This info is saved per owner, per token, per spender and all signed over in the permit message
/// @dev Setting amount to type(uint160).max sets an unlimited approval
struct PackedAllowance {
// amount allowed
uint160 amount;
// permission expiry
uint48 expiration;
// an incrementing value indexed per owner,token,and spender for each signature
uint48 nonce;
}
/// @notice A token spender pair.
struct TokenSpenderPair {
// the token the spender is approved
address token;
// the spender address
address spender;
}
/// @notice Details for a token transfer.
struct AllowanceTransferDetails {
// the owner of the token
address from;
// the recipient of the token
address to;
// the amount of the token
uint160 amount;
// the token to be transferred
address token;
}
/// @notice A mapping from owner address to token address to spender address to PackedAllowance struct, which contains details and conditions of the approval.
/// @notice The mapping is indexed in the above order see: allowance[ownerAddress][tokenAddress][spenderAddress]
/// @dev The packed slot holds the allowed amount, expiration at which the allowed amount is no longer valid, and current nonce thats updated on any signature based approvals.
function allowance(
address user,
address token,
address spender
) external view returns (uint160 amount, uint48 expiration, uint48 nonce);
/// @notice Approves the spender to use up to amount of the specified token up until the expiration
/// @param token The token to approve
/// @param spender The spender address to approve
/// @param amount The approved amount of the token
/// @param expiration The timestamp at which the approval is no longer valid
/// @dev The packed allowance also holds a nonce, which will stay unchanged in approve
/// @dev Setting amount to type(uint160).max sets an unlimited approval
function approve(
address token,
address spender,
uint160 amount,
uint48 expiration
) external;
/// @notice Permit a spender to a given amount of the owners token via the owner's EIP-712 signature
/// @dev May fail if the owner's nonce was invalidated in-flight by invalidateNonce
/// @param owner The owner of the tokens being approved
/// @param permitSingle Data signed over by the owner specifying the terms of approval
/// @param signature The owner's signature over the permit data
function permit(
address owner,
PermitSingle memory permitSingle,
bytes calldata signature
) external;
/// @notice Permit a spender to the signed amounts of the owners tokens via the owner's EIP-712 signature
/// @dev May fail if the owner's nonce was invalidated in-flight by invalidateNonce
/// @param owner The owner of the tokens being approved
/// @param permitBatch Data signed over by the owner specifying the terms of approval
/// @param signature The owner's signature over the permit data
function permit(
address owner,
PermitBatch memory permitBatch,
bytes calldata signature
) external;
/// @notice Transfer approved tokens from one address to another
/// @param from The address to transfer from
/// @param to The address of the recipient
/// @param amount The amount of the token to transfer
/// @param token The token address to transfer
/// @dev Requires the from address to have approved at least the desired amount
/// of tokens to msg.sender.
function transferFrom(
address from,
address to,
uint160 amount,
address token
) external;
/// @notice Transfer approved tokens in a batch
/// @param transferDetails Array of owners, recipients, amounts, and tokens for the transfers
/// @dev Requires the from addresses to have approved at least the desired amount
/// of tokens to msg.sender.
function transferFrom(
AllowanceTransferDetails[] calldata transferDetails
) external;
/// @notice Enables performing a "lockdown" of the sender's Permit2 identity
/// by batch revoking approvals
/// @param approvals Array of approvals to revoke.
function lockdown(TokenSpenderPair[] calldata approvals) external;
/// @notice Invalidate nonces for a given (token, spender) pair
/// @param token The token to invalidate nonces for
/// @param spender The spender to invalidate nonces for
/// @param newNonce The new nonce to set. Invalidates all nonces less than it.
/// @dev Can't invalidate more than 2**16 nonces per transaction.
function invalidateNonces(
address token,
address spender,
uint48 newNonce
) external;
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.17;
interface IEIP712 {
function DOMAIN_SEPARATOR() external view returns (bytes32);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 amount) external returns (bool);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*/
interface IERC20Permit {
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}
// SPDX-License-Identifier: BUSL-1.1
/**
* @title Portfolio for the Portfolio
* @author Velvet.Capital
* @notice This contract is used by the user to deposit and withdraw from the portfolio
* @dev This contract includes functionalities:
* 1. Deposit in the particular fund
* 2. Withdraw from the fund
*/
pragma solidity 0.8.17;
import {FunctionParameters} from "../../FunctionParameters.sol";
import {IPriceOracle} from "../../oracle/IPriceOracle.sol";
import {IAllowanceTransfer} from "./IAllowanceTransfer.sol";
interface IPortfolio {
function vault() external view returns (address);
function feeModule() external view returns (address);
function protocolConfig() external view returns (address);
function tokenExclusionManager() external view returns (address);
function accessController() external view returns (address);
function paused() external view returns (bool);
function assetManagementConfig() external view returns (address);
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(
address owner,
address spender
) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(
address from,
address to,
uint256 amount
) external returns (bool);
function init(
FunctionParameters.PortfolioInitData calldata initData
) external;
/**
* @dev Sets up the initial assets for the pool.
* @param tokens Underlying tokens to initialize the pool with
*/
function initToken(address[] calldata tokens) external;
// For Minting Shares
function mintShares(address _to, uint256 _amount) external;
function pullFromVault(address _token, uint256 _amount, address _to) external;
/**
* @notice The function swaps BNB into the portfolio tokens after a user makes an deposit
* @dev The output of the swap is converted into USD to get the actual amount after slippage to calculate
the portfolio token amount to mint
* @dev (tokenBalance, vaultBalance) has to be calculated before swapping for the _mintShareAmount function
because during the swap the amount will change but the portfolio token balance is still the same
(before minting)
*/
function multiTokenDeposit(
uint256[] calldata depositAmounts,
uint256 _minMintAmount,
IAllowanceTransfer.PermitBatch calldata _permit,
bytes calldata _signature
) external;
/**
* @notice Allows a specified depositor to deposit tokens into the fund through a multi-token deposit.
* The deposited tokens are added to the vault, and the user is minted portfolio tokens representing their share.
* @param _depositFor The address of the user the deposit is being made for.
* @param depositAmounts An array of amounts corresponding to each token the user wishes to deposit.
* @param _minMintAmount The minimum amount of portfolio tokens the user expects to receive for their deposit, protecting against slippage.
*/
function multiTokenDepositFor(
address _depositFor,
uint256[] calldata depositAmounts,
uint256 _minMintAmount
) external;
/**
* @notice The function swaps the amount of portfolio tokens represented by the amount of portfolio token back to
BNB and returns it to the user and burns the amount of portfolio token being withdrawn
* @param _portfolioTokenAmount The portfolio token amount the user wants to withdraw from the fund
*/
function multiTokenWithdrawal(uint256 _portfolioTokenAmount) external;
/**
* @notice Allows an approved user to withdraw portfolio tokens on behalf of another user.
* @param _withdrawFor The address of the user for whom the withdrawal is being made.
* @param _portfolioTokenAmount The amount of portfolio tokens to withdraw.
*/
function multiTokenWithdrawalFor(
address _withdrawFor,
address _tokenReceiver,
uint256 _portfolioTokenAmount
) external;
/**
@notice The function returns lastRebalanced time
*/
function getLastRebalance() external view returns (uint256);
/**
@notice The function returns lastPaused time
*/
function getLastPaused() external view returns (uint256);
function getTokens() external view returns (address[] memory);
function updateTokenList(address[] memory tokens) external;
function userLastDepositTime(address owner) external view returns (uint256);
function _checkCoolDownPeriod(address _user) external view;
function getTokenBalancesOf(
address[] memory,
address
) external view returns (uint256[] memory);
function getVaultValueInUSD(
IPriceOracle,
address[] memory,
uint256,
address
) external view returns (uint256);
function _calculateMintAmount(uint256, uint256) external returns (uint256);
function claimRewardTokens(
address _target,
bytes memory _claimCalldata
) external;
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.17;
import {AggregatorV2V3Interface} from "@chainlink/contracts/src/v0.8/interfaces/AggregatorV2V3Interface.sol";
interface IPriceOracle {
function WETH() external returns (address);
function _addFeed(
address base,
address quote,
AggregatorV2V3Interface aggregator
) external;
function convertToUSD18Decimals(
address _base,
uint256 amountIn
) external view returns (uint256 amountOut);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)
pragma solidity ^0.8.0;
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuard {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor() {
_status = _NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
// On the first call to nonReentrant, _status will be _NOT_ENTERED
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_status = _ENTERED;
}
function _nonReentrantAfter() private {
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = _NOT_ENTERED;
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == _ENTERED;
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../extensions/IERC20Permit.sol";
import "../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using Address for address;
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
/**
* @dev Deprecated. This function has issues similar to the ones found in
* {IERC20-approve}, and its usage is discouraged.
*
* Whenever possible, use {safeIncreaseAllowance} and
* {safeDecreaseAllowance} instead.
*/
function safeApprove(IERC20 token, address spender, uint256 value) internal {
// safeApprove should only be called when setting an initial allowance,
// or when resetting it to zero. To increase and decrease it, use
// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
require(
(value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Compatible with tokens that require the approval to be set to
* 0 before setting it to a non-zero value.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
* Revert on invalid signature.
*/
function safePermit(
IERC20Permit token,
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
uint256 nonceBefore = token.nonces(owner);
token.permit(owner, spender, value, deadline, v, r, s);
uint256 nonceAfter = token.nonces(owner);
require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
// and not revert is the subcall reverts.
(bool success, bytes memory returndata) = address(token).call(data);
return
success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
}
}
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity >=0.6.0;
// helper methods for interacting with ERC20 tokens and sending ETH that do not consistently return true/false
library TransferHelper {
function safeApprove(
address token,
address to,
uint256 value
) internal {
// bytes4(keccak256(bytes('approve(address,uint256)')));
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x095ea7b3, to, value));
require(
success && (data.length == 0 || abi.decode(data, (bool))),
'TransferHelper::safeApprove: approve failed'
);
}
function safeTransfer(
address token,
address to,
uint256 value
) internal {
// bytes4(keccak256(bytes('transfer(address,uint256)')));
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(0xa9059cbb, to, value));
require(
success && (data.length == 0 || abi.decode(data, (bool))),
'TransferHelper::safeTransfer: transfer failed'
);
}
function safeTransferFrom(
address token,
address from,
address to,
uint256 value
) internal {
// bytes4(keccak256(bytes('transferFrom(address,address,uint256)')));
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x23b872dd, from, to, value));
require(
success && (data.length == 0 || abi.decode(data, (bool))),
'TransferHelper::transferFrom: transferFrom failed'
);
}
function safeTransferETH(address to, uint256 value) internal {
(bool success, ) = to.call{value: value}(new bytes(0));
require(success, 'TransferHelper::safeTransferETH: ETH transfer failed');
}
}
{
"compilationTarget": {
"contracts/bundle/DepositBatch.sol": "DepositBatch"
},
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[],"name":"CallFailed","type":"error"},{"inputs":[],"name":"InvalidBalance","type":"error"},{"inputs":[],"name":"InvalidBalanceDiff","type":"error"},{"inputs":[],"name":"InvalidLength","type":"error"},{"inputs":[],"name":"TransferFailed","type":"error"},{"inputs":[{"components":[{"internalType":"uint256","name":"_minMintAmount","type":"uint256"},{"internalType":"uint256","name":"_depositAmount","type":"uint256"},{"internalType":"address","name":"_target","type":"address"},{"internalType":"address","name":"_depositToken","type":"address"},{"internalType":"bytes[]","name":"_callData","type":"bytes[]"}],"internalType":"struct FunctionParameters.BatchHandler","name":"data","type":"tuple"},{"internalType":"address","name":"user","type":"address"}],"name":"multiTokenSwapAndDeposit","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"_minMintAmount","type":"uint256"},{"internalType":"uint256","name":"_depositAmount","type":"uint256"},{"internalType":"address","name":"_target","type":"address"},{"internalType":"address","name":"_depositToken","type":"address"},{"internalType":"bytes[]","name":"_callData","type":"bytes[]"}],"internalType":"struct FunctionParameters.BatchHandler","name":"data","type":"tuple"}],"name":"multiTokenSwapETHAndTransfer","outputs":[],"stateMutability":"payable","type":"function"},{"stateMutability":"payable","type":"receive"}]