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/zaps/FlashRepay.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
import {ISize} from "@size/src/market/interfaces/ISize.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {DepositParams} from "@size/src/market/libraries/actions/Deposit.sol";
import {DexSwapUpgradeable, SwapParams} from "src/utils/DexSwapUpgradeable.sol";
import {Errors} from "@size/src/market/libraries/Errors.sol";
import {DataView} from "@size/src/market/SizeViewData.sol";
import {MorphoFlashLoanReceiverBaseUpgradeable} from "src/utils/MorphoFlashLoanReceiverBaseUpgradeable.sol";
import {MulticallUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/MulticallUpgradeable.sol";
import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import {IFlashRepayFactory} from "src/zaps/IFlashRepayFactory.sol";
import {DebtPosition} from "@size/src/market/libraries/LoanLibrary.sol";
import {RepayParams} from "@size/src/market/libraries/actions/Repay.sol";
import {WithdrawOnBehalfOfParams, WithdrawParams} from "@size/src/market/libraries/actions/Withdraw.sol";
string constant FLASH_REPAY_VERSION = "v0.1";
/// @title FlashRepay
/// @custom:security-contact security@size.credit
/// @author Size (https://size.credit/)
/// @notice A contract that allows users to loop using flash loans
contract FlashRepay is
MulticallUpgradeable,
MorphoFlashLoanReceiverBaseUpgradeable,
DexSwapUpgradeable,
OwnableUpgradeable
{
using SafeERC20 for IERC20;
using SafeERC20 for IERC20Metadata;
// STORAGE
/// @custom:storage-location erc7201:size.storage.FlashRepay
struct FlashRepayStorage {
IFlashRepayFactory _flashRepayFactory;
}
// keccak256(abi.encode(uint256(keccak256("size.storage.FlashRepay")) - 1)) & ~bytes32(uint256(0xff));
// forge-lint: disable-next-line(screaming-snake-case-const)
bytes32 private constant FlashRepayStorageLocation =
0x934d637bd9f2a47553c5c598134dd97bfee56dd0b6bcd46cf3c09789c669c900;
function _getFlashRepayStorage() private pure returns (FlashRepayStorage storage $) {
assembly {
$.slot := FlashRepayStorageLocation
}
}
struct OperationParams {
ISize market;
uint256 debtPositionId;
uint256 collateralAmount;
uint256 futureValue;
SwapParams[] swapParamsArray;
}
// CONSTRUCTOR/INITIALIZER
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}
function initialize(IFlashRepayFactory _flashRepayFactory, address _owner) public initializer {
if (address(_flashRepayFactory) == address(0)) {
revert Errors.NULL_ADDRESS();
}
FlashRepayStorage storage $ = _getFlashRepayStorage();
$._flashRepayFactory = _flashRepayFactory;
__Multicall_init();
__MorphoFlashLoanReceiverBase_init(address(_flashRepayFactory.morpho()));
__Ownable_init(_owner);
__DexSwapUpgradeable_init(
address(_flashRepayFactory.uniswapV2Router()),
address(_flashRepayFactory.uniswapV3Router()),
address(_flashRepayFactory.pendleRouter()),
address(_flashRepayFactory.pendleMarketFactory())
);
}
/// @dev Users must first `setAuthorization` with `WITHDRAW` to this contract
function repayWithCollateral(
ISize market,
uint256 debtPositionId,
uint256 collateralAmount,
SwapParams[] calldata swapParamsArray
) external onlyOwner {
if (!flashRepayFactory().sizeFactory().isMarket(address(market))) {
revert Errors.INVALID_MARKET(address(market));
}
DebtPosition memory debtPosition = market.getDebtPosition(debtPositionId);
DataView memory data = market.data();
OperationParams memory operationParams = OperationParams({
market: market,
debtPositionId: debtPositionId,
collateralAmount: collateralAmount,
futureValue: debtPosition.futureValue,
swapParamsArray: swapParamsArray
});
_flashLoan(address(data.underlyingBorrowToken), debtPosition.futureValue, abi.encode(operationParams));
uint256 balance = data.underlyingCollateralToken.balanceOf(address(this));
if (balance > 0) {
data.underlyingCollateralToken.forceApprove(address(market), balance);
market.deposit(
DepositParams({token: address(data.underlyingCollateralToken), amount: balance, to: owner()})
);
}
}
function rescueTokens(address token, address to) external onlyOwner {
uint256 amount = IERC20(token).balanceOf(address(this));
IERC20(token).safeTransfer(to, amount);
}
function _flashLoanCallback(address, uint256, bytes memory params) internal override {
OperationParams memory operationParams = abi.decode(params, (OperationParams));
DataView memory data = operationParams.market.data();
IERC20Metadata(data.underlyingBorrowToken).forceApprove(
address(operationParams.market), operationParams.futureValue
);
operationParams.market.deposit(
DepositParams({
token: address(data.underlyingBorrowToken),
amount: operationParams.futureValue,
to: address(this)
})
);
operationParams.market.repay(RepayParams({debtPositionId: operationParams.debtPositionId, borrower: owner()}));
operationParams.market.withdrawOnBehalfOf(
WithdrawOnBehalfOfParams({
params: WithdrawParams({
token: address(data.underlyingCollateralToken),
amount: operationParams.collateralAmount,
to: address(this)
}),
onBehalfOf: owner()
})
);
_swap(operationParams.swapParamsArray);
}
function version() external pure returns (string memory) {
return FLASH_REPAY_VERSION;
}
function flashRepayFactory() public view returns (IFlashRepayFactory) {
return _getFlashRepayStorage()._flashRepayFactory;
}
}
"
},
"lib/size-solidity/src/market/interfaces/ISize.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
import {SellCreditLimitParams} from "@src/market/libraries/actions/SellCreditLimit.sol";
import {SellCreditMarketParams} from "@src/market/libraries/actions/SellCreditMarket.sol";
import {ClaimParams} from "@src/market/libraries/actions/Claim.sol";
import {BuyCreditLimitParams} from "@src/market/libraries/actions/BuyCreditLimit.sol";
import {LiquidateParams} from "@src/market/libraries/actions/Liquidate.sol";
import {DepositParams} from "@src/market/libraries/actions/Deposit.sol";
import {WithdrawParams} from "@src/market/libraries/actions/Withdraw.sol";
import {LiquidateWithReplacementParams} from "@src/market/libraries/actions/LiquidateWithReplacement.sol";
import {RepayParams} from "@src/market/libraries/actions/Repay.sol";
import {SelfLiquidateParams} from "@src/market/libraries/actions/SelfLiquidate.sol";
import {CompensateParams} from "@src/market/libraries/actions/Compensate.sol";
import {
InitializeFeeConfigParams,
InitializeOracleParams,
InitializeRiskConfigParams
} from "@src/market/libraries/actions/Initialize.sol";
import {PartialRepayParams} from "@src/market/libraries/actions/PartialRepay.sol";
import {IMulticall} from "@src/market/interfaces/IMulticall.sol";
import {ISizeView} from "@src/market/interfaces/ISizeView.sol";
import {BuyCreditMarketParams} from "@src/market/libraries/actions/BuyCreditMarket.sol";
import {SetCopyLimitOrderConfigsParams} from "@src/market/libraries/actions/SetCopyLimitOrderConfigs.sol";
import {SetUserConfigurationParams} from "@src/market/libraries/actions/SetUserConfiguration.sol";
import {ISizeAdmin} from "@src/market/interfaces/ISizeAdmin.sol";
import {ISizeV1_7} from "@src/market/interfaces/v1.7/ISizeV1_7.sol";
import {ISizeV1_8} from "@src/market/interfaces/v1.8/ISizeV1_8.sol";
string constant VERSION = "v1.8";
/// @title ISize
/// @custom:security-contact security@size.credit
/// @author Size (https://size.credit/)
/// @notice This interface is the main interface for all user-facing methods of the Size protocol
/// @dev All functions are `payable` to allow for ETH deposits in a `multicall` pattern.
/// See `Multicall.sol`
interface ISize is ISizeView, ISizeAdmin, IMulticall, ISizeV1_7, ISizeV1_8 {
/// @notice Deposit underlying borrow/collateral tokens to the protocol (e.g. USDC, WETH)
/// Borrow tokens are always deposited into the Aave Variable Pool or User Vault
/// Collateral tokens are deposited into the Size contract
/// @dev The caller must approve the transfer of the token to the protocol.
/// This function mints 1:1 Deposit Tokens (e.g. szaUSDC, szETH) in exchange of the deposited tokens
/// @param params DepositParams struct containing the following fields:
/// - address token: The address of the token to deposit
/// - uint256 amount: The amount of tokens to deposit
/// - uint256 to: The recipient of the deposit
function deposit(DepositParams calldata params) external payable;
/// @notice Withdraw underlying borrow/collateral tokens from the protocol (e.g. USDC, WETH)
/// Borrow tokens are always withdrawn from the Aave Variable Pool or User Vault
/// Collateral tokens are withdrawn from the Size contract
/// @dev This function burns 1:1 Deposit Tokens (e.g. szaUSDC, szETH) in exchange of the withdrawn tokens
/// @param params WithdrawParams struct containing the following fields:
/// - address token: The address of the token to withdraw
/// - uint256 amount: The amount of tokens to withdraw (in decimals, e.g. 1_000e6 for 1000 USDC or 10e18 for 10 WETH)
/// - uint256 to: The recipient of the withdrawal
function withdraw(WithdrawParams calldata params) external payable;
/// @notice Places a new loan offer in the orderbook
/// @param params BuyCreditLimitParams struct containing the following fields:
/// - uint256 maxDueDate: The maximum due date of the loan (e.g., 1712188800 for April 4th, 2024)
/// - YieldCurve curveRelativeTime: The yield curve for the loan offer, a struct containing the following fields:
/// - uint256[] tenors: The relative timestamps of the yield curve (for example, [30 days, 60 days, 90 days])
/// - int256[] aprs: The aprs of the yield curve (for example, [0.05e18, 0.07e18, 0.08e18] to represent 5% APR, 7% APR, and 8% APR, linear interest, respectively)
/// - uint256[] marketRateMultipliers: The market rate multipliers of the yield curve (for example, [1e18, 1.2e18, 1.3e18] to represent 100%, 120%, and 130% of the market borrow rate, respectively)
function buyCreditLimit(BuyCreditLimitParams calldata params) external payable;
/// @notice Places a new borrow offer in the orderbook
/// @param params SellCreditLimitParams struct containing the following fields:
/// - YieldCurve curveRelativeTime: The yield curve for the borrow offer, a struct containing the following fields:
/// - uint256[] tenors: The relative timestamps of the yield curve (for example, [30 days, 60 days, 90 days])
/// - int256[] aprs: The aprs of the yield curve (for example, [0.05e18, 0.07e18, 0.08e18] to represent 5% APR, 7% APR, and 8% APR, linear interest, respectively)
/// - uint256[] marketRateMultipliers: The market rate multipliers of the yield curve (for example, [0.99e18, 1e18, 1.1e18] to represent 99%, 100%, and 110% of the market borrow rate, respectively)
function sellCreditLimit(SellCreditLimitParams calldata params) external payable;
/// @notice Obtains credit via lending or buying existing credit
/// @param params BuyCreditMarketParams struct containing the following fields:
/// - address borrower: The address of the borrower (optional, for lending)
/// - uint256 creditPositionId: The id of the credit position to buy (optional, for buying credit)
/// - uint256 tenor: The tenor of the loan
/// - uint256 amount: The amount of tokens to lend or credit to buy
/// - bool exactAmountIn: Indicates if the amount is the value to be transferred or used to calculate the transfer amount
/// - uint256 deadline: The maximum timestamp for the transaction to be executed
/// - uint256 minAPR: The minimum APR the caller is willing to accept
function buyCreditMarket(BuyCreditMarketParams calldata params) external payable;
/// @notice Sells credit via borrowing or exiting an existing credit position
/// This function can be used both for selling an existing credit or to borrow by creating a DebtPosition/CreditPosition pair
/// @dev Order "takers" are the ones who pay the rounding, since "makers" are the ones passively waiting for an order to be matched
// The caller may pass type(uint256).max as the creditPositionId in order to represent "mint a new DebtPosition/CreditPosition pair"
/// @param params SellCreditMarketParams struct containing the following fields:
/// - address lender: The address of the lender
/// - uint256 creditPositionId: The id of a credit position to be sold
/// - uint256 amount: The amount of tokens to borrow (in decimals, e.g. 1_000e6 for 1000 aUSDC)
/// - uint256 tenor: The tenor of the loan
/// - uint256 deadline: The maximum timestamp for the transaction to be executed
/// - uint256 maxAPR: The maximum APR the caller is willing to accept
/// - bool exactAmountIn: this flag indicates if the amount argument represents either credit (true) or cash (false)
/// - uint256 collectionId: The collection id. If collectionId is RESERVED_ID, selects the user-defined yield curve
/// - address rateProvider: The rate provider. If collectionId is RESERVED_ID, selects the user-defined yield curve
function sellCreditMarket(SellCreditMarketParams calldata params) external payable;
/// @notice Repay a debt position by transferring the amount due of borrow tokens to the protocol, which are deposited to the Variable Pool for the lenders to claim
/// Partial repayment are currently unsupported
/// @dev The Variable Pool liquidity index is snapshotted at the time of the repayment in order to calculate the accrued interest for lenders to claim
/// @param params RepayParams struct containing the following fields:
/// - uint256 debtPositionId: The id of the debt position to repay
function repay(RepayParams calldata params) external payable;
/// @notice Claim the repayment of a loan with accrued interest from the Variable Pool
/// @dev Both ACTIVE and OVERDUE loans can't be claimed because the money is not in the protocol yet.
/// CLAIMED loans can't be claimed either because its credit has already been consumed entirely either by a previous claim or by exiting before
/// @param params ClaimParams struct containing the following fields:
/// - uint256 creditPositionId: The id of the credit position to claim
function claim(ClaimParams calldata params) external payable;
/// @notice Liquidate a debt position
/// In case of a protifable liquidation, part of the collateral remainder is split between the protocol and the liquidator
/// The split is capped by the crLiquidation parameter (otherwise, the split for overdue loans could be too much)
/// If the loan is overdue, a liquidator is charged from the borrower
/// @param params LiquidateParams struct containing the following fields:
/// - uint256 debtPositionId: The id of the debt position to liquidate
/// - uint256 minimumCollateralProfit: The minimum collateral profit that the liquidator is willing to accept from the borrower (keepers might choose to pass a value below 100% of the cash they bring and take the risk of liquidating unprofitably)
/// @return liquidatorProfitCollateralToken The amount of collateral tokens the the fee recipient received from the liquidation
function liquidate(LiquidateParams calldata params)
external
payable
returns (uint256 liquidatorProfitCollateralToken);
/// @notice Self liquidate a credit position that is undercollateralized
/// The lender cancels an amount of debt equivalent to their credit and a percentage of the protocol fees
/// @dev The user is prevented to self liquidate if a regular liquidation would be profitable
/// @param params SelfLiquidateParams struct containing the following fields:
/// - uint256 creditPositionId: The id of the credit position to self-liquidate
function selfLiquidate(SelfLiquidateParams calldata params) external payable;
/// @notice Liquidate a debt position with a replacement borrower
/// @dev This function works exactly like `liquidate`, with an added logic of replacing the borrower on the storage
/// When liquidating with replacement, nothing changes from the lenders' perspective, but a spread is created between the previous borrower rate and the new borrower rate.
/// As a result of the spread of these borrow aprs, the protocol is able to profit from the liquidation. Since the choice of the borrower impacts on the protocol's profit, this method is permissioned
/// @param params LiquidateWithReplacementParams struct containing the following fields:
/// - uint256 debtPositionId: The id of the debt position to liquidate
/// - uint256 minimumCollateralProfit: The minimum collateral profit that the liquidator is willing to accept from the borrower (keepers might choose to pass a value below 100% of the cash they bring and take the risk of liquidating unprofitably)
/// - address borrower: The address of the replacement borrower
/// - uint256 deadline: The maximum timestamp for the transaction to be executed
/// - uint256 minAPR: The minimum APR the caller is willing to accept
/// @return liquidatorProfitCollateralToken The amount of collateral tokens liquidator received from the liquidation
/// @return liquidatorProfitBorrowToken The amount of borrow tokens liquidator received from the liquidation
function liquidateWithReplacement(LiquidateWithReplacementParams calldata params)
external
payable
returns (uint256 liquidatorProfitCollateralToken, uint256 liquidatorProfitBorrowToken);
/// @notice Compensate a borrower's debt with his credit in another loan
/// The compensation can not exceed both 1) the credit the lender of `creditPositionWithDebtToRepayId` to the borrower and 2) the credit the lender of `creditPositionToCompensateId`
// @dev The caller may pass type(uint256).max as the creditPositionId in order to represent "mint a new DebtPosition/CreditPosition pair"
/// @param params CompensateParams struct containing the following fields:
/// - uint256 creditPositionWithDebtToRepayId: The id of the credit position ID with debt to repay
/// - uint256 creditPositionToCompensateId: The id of the credit position to compensate
/// - uint256 amount: The amount of tokens to compensate (in decimals, e.g. 1_000e6 for 1000 aUSDC)
function compensate(CompensateParams calldata params) external payable;
/// @notice Partial repay a debt position by selecting a specific CreditPosition
/// @param params PartialRepayParams struct containing the following fields:
/// - uint256 creditPositionWithDebtToRepayId: The id of the credit position with debt to repay
/// - uint256 amount: The amount of tokens to repay (in decimals, e.g. 1_000e6 for 1000 aUSDC)
/// - address borrower: The address of the borrower
/// @dev The partial repay amount should be less than the debt position future value
function partialRepay(PartialRepayParams calldata params) external payable;
/// @notice Set the credit positions for sale
/// @dev By default, all created creadit positions are for sale.
/// Users who want to disable the sale of all or specific credit positions can do so by calling this function.
/// By default, all users CR to open a position is crOpening. Users who want to increase their CR opening limit can do so by calling this function.
/// Note: this function was updated in v1.8 to accept a `vault` parameter.
/// Although this function is market-specific, it will change a NonTransferrableRebasingTokenVault state that will be reflected on all markets.
/// @param params SetUserConfigurationParams struct containing the following fields:
/// - address vault: The address of the user vault
/// - uint256 openingLimitBorrowCR: The opening limit borrow collateral ratio, which indicates the maximum CR the borrower is willing to accept after their offer is picked by a lender
/// - bool allCreditPositionsForSaleDisabled: This global flag indicates if all credit positions should be set for sale or not
/// - bool creditPositionIdsForSale: This flag indicates if the creditPositionIds array should be set for sale or not
/// - uint256[] creditPositionIds: The id of the credit positions
function setUserConfiguration(SetUserConfigurationParams calldata params) external payable;
/// @notice Set the copy limit order configs for a user
/// @param params SetCopyLimitOrderConfigsParams struct containing the following fields:
/// - CopyLimitOrderConfig copyLoanOfferConfig: The loan offer copy parameters
/// - uint256 minTenor: The minimum tenor of the loan offer to copy (0 means use copy yield curve tenor lower bound)
/// - uint256 maxTenor: The maximum tenor of the loan offer to copy (type(uint256).max means use copy yield curve tenor upper bound)
/// - uint256 minAPR: The minimum APR of the loan offer to copy (0 means use copy yield curve APR lower bound)
/// - uint256 maxAPR: The maximum APR of the loan offer to copy (type(uint256).max means use copy yield curve APR upper bound)
/// - int256 offsetAPR: The offset APR relative to the copied loan offer
/// - CopyLimitOrderConfig copyBorrowOfferConfig: The borrow offer copy parameters
/// - uint256 minTenor: The minimum tenor of the borrow offer to copy (0 means use copy yield curve tenor lower bound)
/// - uint256 maxTenor: The maximum tenor of the borrow offer to copy (type(uint256).max means use copy yield curve tenor upper bound)
/// - uint256 minAPR: The minimum APR of the borrow offer to copy (0 means use copy yield curve APR lower bound)
/// - uint256 maxAPR: The maximum APR of the borrow offer to copy (type(uint256).max means use copy yield curve APR upper bound)
/// - int256 offsetAPR: The offset APR relative to the copied borrow offer
/// @dev Does not erase the user's loan offer and borrow offer
/// To specify "no copy", pass a null CopyLimitOrderConfig except for offsetAPR, since a completely null CopyLimitOrderConfig
/// will default to the curator-defined CopyLimitOrderConfig for that market.
function setCopyLimitOrderConfigs(SetCopyLimitOrderConfigsParams calldata params) external payable;
}
"
},
"lib/size-solidity/lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC-20 standard as defined in the ERC.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
}
"
},
"lib/size-solidity/lib/openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC-20 standard.
*/
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}
"
},
"lib/size-solidity/lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC1363} from "../../../interfaces/IERC1363.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC-20 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 {
/**
* @dev An operation with an ERC-20 token failed.
*/
error SafeERC20FailedOperation(address token);
/**
* @dev Indicates a failed `decreaseAllowance` request.
*/
error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
/**
* @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.encodeCall(token.transfer, (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.encodeCall(token.transferFrom, (from, to, value)));
}
/**
* @dev Variant of {safeTransfer} that returns a bool instead of reverting if the operation is not successful.
*/
function trySafeTransfer(IERC20 token, address to, uint256 value) internal returns (bool) {
return _callOptionalReturnBool(token, abi.encodeCall(token.transfer, (to, value)));
}
/**
* @dev Variant of {safeTransferFrom} that returns a bool instead of reverting if the operation is not successful.
*/
function trySafeTransferFrom(IERC20 token, address from, address to, uint256 value) internal returns (bool) {
return _callOptionalReturnBool(token, abi.encodeCall(token.transferFrom, (from, to, 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.
*
* IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
* smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
* this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
* that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
forceApprove(token, spender, oldAllowance + value);
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
* value, non-reverting calls are assumed to be successful.
*
* IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
* smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
* this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
* that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
unchecked {
uint256 currentAllowance = token.allowance(address(this), spender);
if (currentAllowance < requestedDecrease) {
revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
}
forceApprove(token, spender, currentAllowance - requestedDecrease);
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*
* NOTE: If the token implements ERC-7674, this function will not modify any temporary allowance. This function
* only sets the "standard" allowance. Any temporary allowance will remain active, in addition to the value being
* set here.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Performs an {ERC1363} transferAndCall, with a fallback to the simple {ERC20} transfer if the target has no
* code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* Reverts if the returned value is other than `true`.
*/
function transferAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
if (to.code.length == 0) {
safeTransfer(token, to, value);
} else if (!token.transferAndCall(to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Performs an {ERC1363} transferFromAndCall, with a fallback to the simple {ERC20} transferFrom if the target
* has no code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* Reverts if the returned value is other than `true`.
*/
function transferFromAndCallRelaxed(
IERC1363 token,
address from,
address to,
uint256 value,
bytes memory data
) internal {
if (to.code.length == 0) {
safeTransferFrom(token, from, to, value);
} else if (!token.transferFromAndCall(from, to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no
* code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}.
* Opposedly, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall}
* once without retrying, and relies on the returned value to be true.
*
* Reverts if the returned value is other than `true`.
*/
function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
if (to.code.length == 0) {
forceApprove(token, to, value);
} else if (!token.approveAndCall(to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @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 {_callOptionalReturnBool} that reverts if call fails to meet the requirements.
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
uint256 returnSize;
uint256 returnValue;
assembly ("memory-safe") {
let success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
// bubble errors
if iszero(success) {
let ptr := mload(0x40)
returndatacopy(ptr, 0, returndatasize())
revert(ptr, returndatasize())
}
returnSize := returndatasize()
returnValue := mload(0)
}
if (returnSize == 0 ? address(token).code.length == 0 : returnValue != 1) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @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 silently catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
bool success;
uint256 returnSize;
uint256 returnValue;
assembly ("memory-safe") {
success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
returnSize := returndatasize()
returnValue := mload(0)
}
return success && (returnSize == 0 ? address(token).code.length > 0 : returnValue == 1);
}
}
"
},
"lib/size-solidity/src/market/libraries/actions/Deposit.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {IWETH} from "@src/market/interfaces/IWETH.sol";
import {State} from "@src/market/SizeStorage.sol";
import {Action} from "@src/factory/libraries/Authorization.sol";
import {Errors} from "@src/market/libraries/Errors.sol";
import {Events} from "@src/market/libraries/Events.sol";
struct DepositParams {
// The token to deposit
address token;
// The amount to deposit
uint256 amount;
// The account to deposit the tokens to
address to;
}
struct DepositOnBehalfOfParams {
// The parameters for the deposit
DepositParams params;
// The account to transfer the tokens from
address onBehalfOf;
}
/// @title Deposit
/// @custom:security-contact security@size.credit
/// @author Size (https://size.credit/)
/// @notice Contains the logic for depositing tokens into the protocol
library Deposit {
using SafeERC20 for IERC20Metadata;
using SafeERC20 for IWETH;
/// @notice Validates the deposit parameters
/// @param state The state of the protocol
/// @param externalParams The input parameters for depositing tokens
function validateDeposit(State storage state, DepositOnBehalfOfParams memory externalParams) external view {
DepositParams memory params = externalParams.params;
address onBehalfOf = externalParams.onBehalfOf;
// validate msg.sender
if (!state.data.sizeFactory.isAuthorized(msg.sender, onBehalfOf, Action.DEPOSIT)) {
revert Errors.UNAUTHORIZED_ACTION(msg.sender, onBehalfOf, uint8(Action.DEPOSIT));
}
// validate msg.value
if (
msg.value != 0
&& (msg.value != params.amount || params.token != address(state.data.weth) || onBehalfOf != msg.sender)
) {
revert Errors.INVALID_MSG_VALUE(msg.value);
}
// validate token
if (
params.token != address(state.data.underlyingCollateralToken)
&& params.token != address(state.data.underlyingBorrowToken)
) {
revert Errors.INVALID_TOKEN(params.token);
}
// validate amount
if (params.amount == 0) {
revert Errors.NULL_AMOUNT();
}
// validate to
if (params.to == address(0)) {
revert Errors.NULL_ADDRESS();
}
}
/// @notice Executes the deposit
/// @param state The state of the protocol
/// @param externalParams The input parameters for depositing tokens
/// @dev The actual deposited amount can be lower than the input amount based on the vault deposit/rounding logic
function executeDeposit(State storage state, DepositOnBehalfOfParams memory externalParams) public {
DepositParams memory params = externalParams.params;
address onBehalfOf = externalParams.onBehalfOf;
address from = onBehalfOf;
uint256 amount = params.amount;
if (msg.value > 0) {
// do not trust msg.value (see `Multicall.sol`)
amount = address(this).balance;
// slither-disable-next-line arbitrary-send-eth
state.data.weth.deposit{value: amount}();
state.data.weth.forceApprove(address(this), amount);
from = address(this);
}
if (params.token == address(state.data.underlyingBorrowToken)) {
state.data.underlyingBorrowToken.safeTransferFrom(from, address(this), amount);
state.data.underlyingBorrowToken.forceApprove(address(state.data.borrowTokenVault), amount);
amount = state.data.borrowTokenVault.deposit(params.to, amount);
} else {
state.data.underlyingCollateralToken.safeTransferFrom(from, address(this), amount);
state.data.collateralToken.mint(params.to, amount);
}
emit Events.Deposit(msg.sender, onBehalfOf, params.token, params.to, amount);
}
}
"
},
"src/utils/DexSwapUpgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IPMarketFactoryV3} from "@pendle/contracts/interfaces/IPMarketFactoryV3.sol";
import {IPAllActionV3} from "@pendle/contracts/interfaces/IPAllActionV3.sol";
import {IPPrincipalToken} from "@pendle/contracts/interfaces/IPPrincipalToken.sol";
import {IPYieldToken} from "@pendle/contracts/interfaces/IPYieldToken.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {Errors} from "@size/src/market/libraries/Errors.sol";
import {IUniswapV2Router02} from "src/interfaces/dex/IUniswapV2Router02.sol";
import {IUniswapV3Router} from "src/interfaces/dex/IUniswapV3Router.sol";
import {PeripheryErrors} from "src/libraries/PeripheryErrors.sol";
import {IPMarket} from "@pendle/contracts/interfaces/IPMarket.sol";
import {IStandardizedYield} from "@pendle/contracts/interfaces/IStandardizedYield.sol";
import {
createDefaultApproxParams,
createTokenInputSimple,
createEmptyLimitOrderData
} from "@pendle/contracts/interfaces/IPAllActionTypeV3.sol";
import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
enum SwapMethod {
UniswapV2ExactInput,
UniswapV3ExactInput,
SellPt,
BuyPt,
UniswapV2ExactOutput,
UniswapV3ExactOutput
}
struct SwapParams {
SwapMethod method;
bytes data;
}
struct UniswapV2ExactInputParams {
uint256 amountOutMin;
address[] path;
address to;
uint256 deadline;
}
struct UniswapV3ExactInputParams {
address tokenIn;
address tokenOut;
uint24 fee;
uint160 sqrtPriceLimitX96;
uint256 amountOutMinimum;
}
struct SellPtParams {
address market;
address tokenOut;
uint256 minTokenOut;
}
struct BuyPtParams {
address market;
address tokenIn;
address router;
uint256 minPtOut;
}
struct UniswapV2ExactOutputParams {
uint256 amountOut;
address[] path;
address to;
uint256 deadline;
}
struct UniswapV3ExactOutputParams {
address tokenIn;
address tokenOut;
uint24 fee;
uint160 sqrtPriceLimitX96;
uint256 amountOut;
}
/// @title DexSwapUpgradeable
/// @custom:security-contact security@size.credit
/// @author Size (https://size.credit/)
/// @notice Contract that allows to swap tokens using different DEXs
abstract contract DexSwapUpgradeable is Initializable {
using SafeERC20 for IERC20;
// STORAGE
/// @custom:storage-location erc7201:size.storage.DexSwapUpgradeable
struct DexSwapUpgradeableStorage {
IUniswapV2Router02 _uniswapV2Router;
IUniswapV3Router _uniswapV3Router;
IPAllActionV3 _pendleRouter;
IPMarketFactoryV3 _pendleMarketFactory;
}
// keccak256(abi.encode(uint256(keccak256("size.storage.DexSwapUpgradeable")) - 1)) & ~bytes32(uint256(0xff));
// forge-lint: disable-next-line(screaming-snake-case-const)
bytes32 private constant DexSwapUpgradeableStorageLocation =
0x6f29eb74e25c3cf85e3f0ab46bdb6ded23eee7bc8cc12fc7a785eb63edac3800;
function _getDexSwapUpgradeableStorage() private pure returns (DexSwapUpgradeableStorage storage $) {
assembly {
$.slot := DexSwapUpgradeableStorageLocation
}
}
// INITIALIZER
/// forge-lint: disable-next-line(mixed-case-function)
function __DexSwapUpgradeable_init(
address _uniswapV2Router,
address _uniswapV3Router,
address _pendleRouter,
address _pendleMarketFactory
) internal onlyInitializing {
if (
_uniswapV2Router == address(0) || _uniswapV3Router == address(0) || _pendleRouter == address(0)
|| _pendleMarketFactory == address(0)
) {
revert Errors.NULL_ADDRESS();
}
DexSwapUpgradeableStorage storage $ = _getDexSwapUpgradeableStorage();
$._uniswapV2Router = IUniswapV2Router02(_uniswapV2Router);
$._uniswapV3Router = IUniswapV3Router(_uniswapV3Router);
$._pendleRouter = IPAllActionV3(_pendleRouter);
$._pendleMarketFactory = IPMarketFactoryV3(_pendleMarketFactory);
}
// FUNCTIONS
function _swap(SwapParams[] memory swapParamsArray) internal {
for (uint256 i = 0; i < swapParamsArray.length; i++) {
SwapParams memory swapParams = swapParamsArray[i];
if (swapParams.method == SwapMethod.UniswapV2ExactInput) {
_swapUniswapV2ExactInput(abi.decode(swapParams.data, (UniswapV2ExactInputParams)));
} else if (swapParams.method == SwapMethod.UniswapV3ExactInput) {
_swapUniswapV3ExactInput(abi.decode(swapParams.data, (UniswapV3ExactInputParams)));
} else if (swapParams.method == SwapMethod.SellPt) {
_sellPt(abi.decode(swapParams.data, (SellPtParams)));
} else if (swapParams.method == SwapMethod.BuyPt) {
_buyPt(abi.decode(swapParams.data, (BuyPtParams)));
} else if (swapParams.method == SwapMethod.UniswapV2ExactOutput) {
_swapUniswapV2ExactOutput(abi.decode(swapParams.data, (UniswapV2ExactOutputParams)));
} else if (swapParams.method == SwapMethod.UniswapV3ExactOutput) {
_swapUniswapV3ExactOutput(abi.decode(swapParams.data, (UniswapV3ExactOutputParams)));
} else {
revert PeripheryErrors.INVALID_SWAP_METHOD();
}
}
}
function _sellPt(SellPtParams memory params) private {
if (!pendleMarketFactory().isValidMarket(params.market)) {
revert PeripheryErrors.INVALID_PENDLE_MARKET(params.market);
}
(IStandardizedYield SY, IPPrincipalToken PT, IPYieldToken YT) = IPMarket(params.market).readTokens();
address tokenIn = address(PT);
uint256 netPtIn = IERC20(tokenIn).balanceOf(address(this));
// from BoringPtSeller
uint256 netSyOut;
if (PT.isExpired()) {
IERC20(PT).safeTransfer(address(YT), netPtIn);
netSyOut = YT.redeemPY(address(SY));
} else {
IERC20(PT).safeTransfer(params.market, netPtIn);
(netSyOut,) = IPMarket(params.market).swapExactPtForSy(
address(SY), // better gas optimization to transfer SY directly to itself and burn
netPtIn,
""
);
}
SY.redeem(address(this), netSyOut, params.tokenOut, params.minTokenOut, true);
}
function _buyPt(BuyPtParams memory params) private {
if (!pendleMarketFactory().isValidMarket(params.market)) {
revert PeripheryErrors.INVALID_PENDLE_MARKET(params.market);
}
uint256 amountIn = IERC20(params.tokenIn).balanceOf(address(this));
IERC20(params.tokenIn).forceApprove(address(pendleRouter()), amountIn);
pendleRouter().swapExactTokenForPt(
address(this),
address(params.market),
params.minPtOut,
createDefaultApproxParams(),
createTokenInputSimple(params.tokenIn, amountIn),
createEmptyLimitOrderData()
);
}
function _swapUniswapV2ExactInput(UniswapV2ExactInputParams memory params) private {
IERC20(params.path[0]).forceApprove(address(uniswapV2Router()), type(uint256).max);
uint256 amountIn = IERC20(params.path[0]).balanceOf(address(this));
uniswapV2Router().swapExactTokensForTokens(
amountIn, params.amountOutMin, params.path, params.to, params.deadline
);
}
function _swapUniswapV3ExactInput(UniswapV3ExactInputParams memory params) private {
uint256 amountIn = IERC20(params.tokenIn).balanceOf(address(this));
IERC20(params.tokenIn).forceApprove(address(uniswapV3Router()), amountIn);
IUniswapV3Router.ExactInputSingleParams memory swapParams = IUniswapV3Router.ExactInputSingleParams({
tokenIn: params.tokenIn,
tokenOut: params.tokenOut,
fee: params.fee,
recipient: address(this),
amountIn: amountIn,
amountOutMinimum: params.amountOutMinimum,
sqrtPriceLimitX96: params.sqrtPriceLimitX96
});
uniswapV3Router().exactInputSingle(swapParams);
}
function _swapUniswapV2ExactOutput(UniswapV2ExactOutputParams memory params) private {
uint256 amountInMaximum = IERC20(params.path[0]).balanceOf(address(this));
IERC20(params.path[0]).forceApprove(address(uniswapV2Router()), amountInMaximum);
uniswapV2Router().swapTokensForExactTokens(
params.amountOut, amountInMaximum, params.path, params.to, params.deadline
);
}
function _swapUniswapV3ExactOutput(UniswapV3ExactOutputParams memory params) private {
uint256 amountInMaximum = IERC20(params.tokenIn).balanceOf(address(this));
IERC20(params.tokenIn).forceApprove(address(uniswapV3Router()), amountInMaximum);
IUniswapV3Router.ExactOutputSingleParams memory swapParams = IUniswapV3Router.ExactOutputSingleParams({
tokenIn: params.tokenIn,
tokenOut: params.tokenOut,
fee: params.fee,
recipient: address(this),
amountInMaximum: amountInMaximum,
amountOut: params.amountOut,
sqrtPriceLimitX96: params.sqrtPriceLimitX96
});
uniswapV3Router().exactOutputSingle(swapParams);
}
// VIEW FUNCTIONS
function uniswapV2Router() public view returns (IUniswapV2Router02) {
return _getDexSwapUpgradeableStorage()._uniswapV2Router;
}
function uniswapV3Router() public view returns (IUniswapV3Router) {
return _getDexSwapUpgradeableStorage()._uniswapV3Router;
}
function pendleRouter() public view returns (IPAllActionV3) {
return _getDexSwapUpgradeableStorage()._pendleRouter;
}
function pendleMarketFactory() public view returns (IPMarketFactoryV3) {
return _getDexSwapUpgradeableStorage()._pendleMarketFactory;
}
}
"
},
"lib/size-solidity/src/market/libraries/Errors.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
/// @title Errors
/// @custom:security-contact security@size.credit
/// @author Size (https://size.credit/)
library Errors {
error MUST_IMPROVE_COLLATERAL_RATIO(address account, uint256 crBefore, uint256 crAfter);
error NULL_ADDRESS();
error NULL_AMOUNT();
error NULL_TENOR();
error NULL_MAX_DUE_DATE();
error NULL_ARRAY();
error NULL_OFFER();
error INVALID_MSG_VALUE(uint256 value);
error INVALID_AMOUNT(uint256 amount);
error INVALID_VAULT(address vault);
error INVALID_ADAPTER(uint256 adapter);
error TENORS_NOT_STRICTLY_INCREASING();
error ARRAY_LENGTHS_MISMATCH();
error INVALID_TOKEN(address token);
error INVALID_KEY(string key);
error INVALID_COLLATERAL_RATIO(uint256 cr);
error INVALID_COLLATERAL_PERCENTAGE_PREMIUM(uint256 percentage);
error INVALID_MAXIMUM_TENOR(uint256 maxTenor);
error VALUE_GREATER_THAN_MAX(uint256 value, uint256 max);
error INVALID_LIQUIDATION_COLLATERAL_RATIO(uint256 crOpening, uint256 crLiquidation);
error INVALID_TENOR_RANGE(uint256 minTenor, uint256 maxTenor);
error INVALID_APR_RANGE(uint256 minAPR, uint256 maxAPR);
error INVALID_ADDRESS(address account);
error PAST_DEADLINE(uint256 deadline);
error PAST_MAX_DUE_DATE(uint256 maxDueDate);
error APR_LOWER_THAN_MIN_APR(uint256 apr, uint256 minAPR);
error APR_GREATER_THAN_MAX_APR(uint256 apr, uint256 maxAPR);
error DUE_DATE_NOT_COMPATIBLE(uint256 dueDate1, uint256 dueDate2);
error DUE_DATE_GREATER_THAN_MAX_DUE_DATE(uint256 dueDate, uint256 maxDueDate);
error TENOR_OUT_OF_RANGE(uint256 tenor, uint256 minTenor, uint256 maxTenor);
error INVERTED_CURVES(address account, uint256 tenor);
error INVALID_POSITION_ID(uint256 positionId);
error INVALID_DEBT_POSITION_ID(uint256 debtPositionId);
error INVALID_CREDIT_POSITION_ID(uint256 creditPositionId);
error INVALID_LENDER(address account);
error INVALID_BORROWER(address account);
error INVALID_OFFER_CONFIGS(
uint256 minTenorBorrowOffer,
uint256 maxTenorBorrowOffer,
uint256 minAPRBorrowOffer,
uint256 maxAPRBorrowOffer,
uint256 minTenorLoanOffer,
uint256 maxTenorLoanOffer,
uint256 minAPRLoanOffer,
uint256 maxAPRLoanOffer
);
error INVALID_LOAN_OFFER(address lender);
error INVALID_BORROW_OFFER(address borrower);
error INVALID_OFFER(address account);
error CREDIT_NOT_FOR_SALE(uint256 creditPositionId);
error NOT_ENOUGH_CREDIT(uint256 credit, uint256 required);
error NOT_ENOUGH_CASH(uint256 cash, uint256 required);
error BORROWER_IS_NOT_LENDER(address borrower, address lender);
error COMPENSATOR_IS_NOT_BORROWER(address compensator, address borrower);
error LIQUIDATOR_IS_NOT_LENDER(address liquidator, address lender);
error NOT_ENOUGH_BORROW_ATOKEN_BALANCE(address account, uint256 balance, uint256 required);
error CREDIT_LOWER_THAN_MINIMUM_CREDIT(uint256 credit, uint256 minimumCreditBorrowToken);
error CREDIT_LOWER_THAN_MINIMUM_CREDIT_OPENING(uint256 credit, uint256 minimumCreditBorrowToken);
error CREDIT_POSITION_ALREADY_CLAIMED(uint256 positionId);
error CREDIT_POSITION_NOT_TRANSFERRABLE(uint256 creditPositionId, uint8 loanStatus, uint256 borrowerCR);
error LOAN_ALREADY_REPAID(uint256 positionId);
error LOAN_NOT_REPAID(uint256 positionId);
error LOAN_NOT_ACTIVE(uint256 positionId);
error LOAN_NOT_LIQUIDATABLE(uint256 debtPositionId, uint256 cr, uint8 loanStatus);
error LOAN_NOT_SELF_LIQUIDATABLE(uint256 creditPositionId, uint256 cr, uint8 loanStatus);
error LIQUIDATE_PROFIT_BELOW_MINIMUM_COLLATERAL_PROFIT(
uint256 liquidatorProfitCollateralToken, uint256 minimumCollateralProfit
);
error CR_BELOW_OPENING_LIMIT_BORROW_CR(address account, uint256 cr, uint256 riskCollateralRatio);
error INVALID_DECIMALS(uint8 decimals);
error INVALID_PRICE(address aggregator, int256 price);
error STALE_PRICE(address aggregator, uint256 updatedAt);
error INVALID_STALE_PRICE_INTERVAL(uint256 a, uint256 b);
error NULL_STALE_PRICE();
error NULL_STALE_RATE();
error STALE_RATE(uint128 updatedAt);
error BORROW_TOKEN_INCREASE_EXCEEDS_DEBT_TOKEN_DECREASE(uint256 borrowTokenIncrease, uint256 debtTokenDecrease);
error NOT_SUPPORTED();
error REINITIALIZE_MIGRATION_EXPECTED_IN_ONE_TRANSACTION(uint256 totalSupply);
error REINITIALIZE_ALL_CLAIMS_PRESERVED(
uint256 newScaledTotalSupplyAfter, uint256 newScaledTotalSupplyBefore, uint256 oldScaledTotalSupply
);
error REINITIALIZE_INSOLVENT(uint256 newTotalSupplyAfter, uint256 newTotalSupplyBefore, uint256 aTokenBalance);
error REINITIALIZE_PER_USER_CHECK(uint256 expected, uint256 actual);
error REINITIALIZE_PER_USER_CHECK_DELTA(uint256 expected, uint256 actual);
error SEQUENCER_DOWN();
error GRACE_PERIOD_NOT_OVER();
error ALREADY_INITIALIZED(address account);
error UNAUTHORIZED(address account);
error UNAUTHORIZED_ACTION(address account, address onBehalfOf, uint8 action);
error INVALID_ACTION(uint8 action);
error INVALID_ACTIONS_BITMAP(uint256 actionsBitmap);
error INVALID_TWAP_WINDOW();
error INVALID_AVERAGE_BLOCK_TIME();
error INVALID_MARKET(address market);
error PAUSED_MARKET(address market);
}
"
},
"lib/size-solidity/src/market/SizeViewData.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
import {IPool} from "@aave/interfaces/IPool.sol";
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {User} from "@src/market/SizeStorage.sol";
import {NonTransferrableRebasingTokenVault} from "@src/market/token/NonTransferrableRebasingTokenVault.sol";
import {NonTransferrableToken} from "@src/market/token/NonTransferrableToken.sol";
struct UserView {
// The user struct
User user;
// The user's account address
address account;
// The user's collateral token balance
uint256 collateralTokenBalance;
// The user's borrow token balance
uint256 borrowTokenBalance;
// The user's debt token balance
uint256 debtBalance;
}
struct DataView {
// The next debt position ID
uint256 nextDebtPositionId;
// The next credit position ID
uint256 nextCreditPositionId;
// The underlying collateral token
IERC20Metadata underlyingCollateralToken;
// The underlying borrow token
IERC20Metadata underlyingBorrowToken;
// The collateral token
NonTransferrableToken collateralToken;
// The default borrow token vault
NonTransferrableRebasingTokenVault borrowTokenVault;
// The debt token
NonTransferrableToken debtToken;
// The variable pool
IPool variablePool;
}
"
},
"src/utils/MorphoFlashLoanReceiverBaseUpgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
import {IMorpho} from "@morpho/src/interfaces/IMorpho.sol";
import {IMorphoFlashLoanCallback} from "@morpho/src/interfaces/IMorphoCallbacks.sol";
import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import {Errors} from "@size/src/market/libraries/Errors.sol";
import {PeripheryErrors} from "src/libraries/PeripheryErrors.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
abstract contract MorphoFlashLoanReceiverBaseUpgradeable is Initializable, IMorphoFlashLoanCallback {
using SafeERC20 for IERC20;
// STORAGE
/// @custom:storage-location erc7201:size.storage.MorphoFlashLoanReceiverBase
struct MorphoFlashLoanReceiverBaseStorage {
IMorpho _morpho;
}
// keccak256(abi.encode(uint256(keccak256("size.storage.MorphoFlashLoanReceiverBase")) - 1)) & ~bytes32(uint256(0xff));
/// forge-lint: disable-next-line(screaming-snake-case-const)
bytes32 private constant MorphoFlashLoanReceiverBaseStorageLocation =
0xe7f9a017cc53a312c77e5e8c1b4f0734320fe14b28476d1b964d8ea53abf4d00;
function _getMorphoFlashLoanReceiverBaseStorage()
private
pure
returns (MorphoFlashLoanReceiverBaseStorage storage $)
{
assembly {
$.slot := MorphoFlashLoanReceiverBaseStorageLocation
}
}
// INITIALIZER
/// forge-lint: disable-next-line(mixed-case-function)
function __MorphoFlashLoanReceiverBase_init(address _morpho) internal onlyInitializing {
if (_morpho == address(0)) {
revert Errors.NULL_ADDRESS();
}
MorphoFlashLoanReceiverBaseStorage storage $ = _getMorphoFlashLoanReceiverBaseStorage();
$._morpho = IMorpho(_morpho);
}
// FUNCTIONS
function onMorphoFlashLoan(uint256 assets, bytes calldata data) external virtual override {
_validateFlashLoanCallbackCaller();
(address token, bytes memory params) = abi.decode(data, (address, bytes));
_flashLoanCallback(token, assets, params);
_approveFlashLoanRepayment(token, assets);
}
function _validateFlashLoanCallbackCaller() private view {
if (msg.sender != address(morpho())) {
revert PeripheryErrors.NOT_MORPHO();
}
}
function _approveFlashLoanRepayment(address token, uint256 amount) private {
IERC20(token).forceApprove(address(morpho()), amount);
}
function _flashLoanCallback(address token, uint256 amount, bytes memory params) internal virtual {}
function _flashLoan(address token, uint256 amount, bytes memory params) internal {
bytes memory externalParams = abi.encode(token, params);
morpho().flashLoan(token, amount, externalParams);
}
// VIEW FUNCTIONS
function morpho() public view returns (IMorpho) {
return _getMorphoFlashLoanReceiverBaseStorage()._morpho;
}
}
"
},
"lib/size-solidity/lib/openzeppelin-contracts-upgradeable/contracts/utils/MulticallUpgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (utils/Multicall.sol)
pragma solidity ^0.8.20;
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {ContextUpgradeable} from "./ContextUpgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Provides a function to batch together multiple calls in a single external call.
*
* Consider any assumption about calldata validation performed by the sender may be violated if it's not especially
* careful about sending transactions invoking {multicall}. For example, a relay address that filters function
* selectors won't filter calls nested within a {multicall} operation.
*
* NOTE: Since 5.0.1 and 4.9.4, this contract identifies non-canonical contexts (i.e. `msg.sender` is not {Context-_msgSender}).
* If a non-canonical context is identified, the following self `delegatecall` appends the last bytes of `msg.data`
* to the subcall. This makes it safe to use with {ERC2771Context}. Contexts that don't affect the resolution of
* {Context-_msgSender} are not propagated to subcalls.
*/
abstract contract MulticallUpgradeable is Initializable, ContextUpgradeable {
function __Multicall_init() internal onlyInitializing {
}
function __Multicall_init_unchained() internal onlyInitializing {
}
/**
* @dev Receives and executes a batch of function calls on this contract.
* @custom:oz-upgrades-unsafe-allow-reachable delegatecall
*/
function multicall(bytes[] calldata data) external virtual returns (bytes[] memory
Submitted on: 2025-09-19 17:34:59
Comments
Log in to comment.
No comments yet.