creation_sourcecode: // SPDX-License-Identifier: MIT
  pragma solidity =0.8.10;
  
  
  
  
  
  
  
  
  
  
  contract MainnetAaveV3Addresses {
      address internal constant AAVE_REWARDS_CONTROLLER_ADDRESS = 0x8164Cc65827dcFe994AB23944CBC90e0aa80bFcb;
      address internal constant DEFAULT_AAVE_MARKET = 0x2f39d218133AFaB8F2B819B1066c7E434Ad94E9e;
      address internal constant AAVE_ORACLE_V3 = 0x54586bE62E3c3580375aE3723C145253060Ca0C2;
  
  }
  
  
  
  
  
  
  interface IAaveProtocolDataProvider {
      /**
       * @notice Returns the user data in a reserve
       * @param asset The address of the underlying asset of the reserve
       * @param user The address of the user
       * @return currentATokenBalance The current AToken balance of the user
       * @return currentStableDebt The current stable debt of the user
       * @return currentVariableDebt The current variable debt of the user
       * @return principalStableDebt The principal stable debt of the user
       * @return scaledVariableDebt The scaled variable debt of the user
       * @return stableBorrowRate The stable borrow rate of the user
       * @return liquidityRate The liquidity rate of the reserve
       * @return stableRateLastUpdated The timestamp of the last update of the user stable rate
       * @return usageAsCollateralEnabled True if the user is using the asset as collateral, false
       *         otherwise
       **/
      function getUserReserveData(
          address asset,
          address user
      )
          external
          view
          returns (
              uint256 currentATokenBalance,
              uint256 currentStableDebt,
              uint256 currentVariableDebt,
              uint256 principalStableDebt,
              uint256 scaledVariableDebt,
              uint256 stableBorrowRate,
              uint256 liquidityRate,
              uint40 stableRateLastUpdated,
              bool usageAsCollateralEnabled
          );
  
      function getSiloedBorrowing(address asset) external view returns (bool);
  
      /**
       * @notice Returns if the pool is paused
       * @param asset The address of the underlying asset of the reserve
       * @return isPaused True if the pool is paused, false otherwise
       **/
      function getPaused(address asset) external view returns (bool isPaused);
  
      /**
       * @notice Returns the configuration data of the reserve
       * @dev Not returning borrow and supply caps for compatibility, nor pause flag
       * @param asset The address of the underlying asset of the reserve
       * @return decimals The number of decimals of the reserve
       * @return ltv The ltv of the reserve
       * @return liquidationThreshold The liquidationThreshold of the reserve
       * @return liquidationBonus The liquidationBonus of the reserve
       * @return reserveFactor The reserveFactor of the reserve
       * @return usageAsCollateralEnabled True if the usage as collateral is enabled, false otherwise
       * @return borrowingEnabled True if borrowing is enabled, false otherwise
       * @return stableBorrowRateEnabled True if stable rate borrowing is enabled, false otherwise
       * @return isActive True if it is active, false otherwise
       * @return isFrozen True if it is frozen, false otherwise
       */
      function getReserveConfigurationData(
          address asset
      )
          external
          view
          returns (
              uint256 decimals,
              uint256 ltv,
              uint256 liquidationThreshold,
              uint256 liquidationBonus,
              uint256 reserveFactor,
              bool usageAsCollateralEnabled,
              bool borrowingEnabled,
              bool stableBorrowRateEnabled,
              bool isActive,
              bool isFrozen
          );
  
      /**
       * @notice Returns the reserve data
       * @param asset The address of the underlying asset of the reserve
       * @return unbacked The amount of unbacked tokens
       * @return accruedToTreasuryScaled The scaled amount of tokens accrued to treasury that is to be minted
       * @return totalAToken The total supply of the aToken
       * @return totalStableDebt The total stable debt of the reserve
       * @return totalVariableDebt The total variable debt of the reserve
       * @return liquidityRate The liquidity rate of the reserve
       * @return variableBorrowRate The variable borrow rate of the reserve
       * @return stableBorrowRate The stable borrow rate of the reserve
       * @return averageStableBorrowRate The average stable borrow rate of the reserve
       * @return liquidityIndex The liquidity index of the reserve
       * @return variableBorrowIndex The variable borrow index of the reserve
       * @return lastUpdateTimestamp The timestamp of the last update of the reserve
       */
      function getReserveData(
          address asset
      )
          external
          view
          returns (
              uint256 unbacked,
              uint256 accruedToTreasuryScaled,
              uint256 totalAToken,
              uint256 totalStableDebt,
              uint256 totalVariableDebt,
              uint256 liquidityRate,
              uint256 variableBorrowRate,
              uint256 stableBorrowRate,
              uint256 averageStableBorrowRate,
              uint256 liquidityIndex,
              uint256 variableBorrowIndex,
              uint40 lastUpdateTimestamp
          );
  }
  
  
  
  
  
  
  library DataTypes {
    struct ReserveData {
      //stores the reserve configuration
      ReserveConfigurationMap configuration;
      //the liquidity index. Expressed in ray
      uint128 liquidityIndex;
      //the current supply rate. Expressed in ray
      uint128 currentLiquidityRate;
      //variable borrow index. Expressed in ray
      uint128 variableBorrowIndex;
      //the current variable borrow rate. Expressed in ray
      uint128 currentVariableBorrowRate;
      //the current stable borrow rate. Expressed in ray
      uint128 currentStableBorrowRate;
      //timestamp of last update
      uint40 lastUpdateTimestamp;
      //the id of the reserve. Represents the position in the list of the active reserves
      uint16 id;
      //aToken address
      address aTokenAddress;
      //stableDebtToken address
      address stableDebtTokenAddress;
      //variableDebtToken address
      address variableDebtTokenAddress;
      //address of the interest rate strategy
      address interestRateStrategyAddress;
      //the current treasury balance, scaled
      uint128 accruedToTreasury;
      //the outstanding unbacked aTokens minted through the bridging feature
      uint128 unbacked;
      //the outstanding debt borrowed against this asset in isolation mode
      uint128 isolationModeTotalDebt;
    }
  
    struct ReserveConfigurationMap {
      //bit 0-15: LTV
      //bit 16-31: Liq. threshold
      //bit 32-47: Liq. bonus
      //bit 48-55: Decimals
      //bit 56: reserve is active
      //bit 57: reserve is frozen
      //bit 58: borrowing is enabled
      //bit 59: stable rate borrowing enabled
      //bit 60: asset is paused
      //bit 61: borrowing in isolation mode is enabled
      //bit 62-63: reserved
      //bit 64-79: reserve factor
      //bit 80-115 borrow cap in whole tokens, borrowCap == 0 => no cap
      //bit 116-151 supply cap in whole tokens, supplyCap == 0 => no cap
      //bit 152-167 liquidation protocol fee
      //bit 168-175 eMode category
      //bit 176-211 unbacked mint cap in whole tokens, unbackedMintCap == 0 => minting disabled
      //bit 212-251 debt ceiling for isolation mode with (ReserveConfiguration::DEBT_CEILING_DECIMALS) decimals
      //bit 252-255 unused
  
      uint256 data;
    }
  
    struct UserConfigurationMap {
      /**
       * @dev Bitmap of the users collaterals and borrows. It is divided in pairs of bits, one pair per asset.
       * The first bit indicates if an asset is used as collateral by the user, the second whether an
       * asset is borrowed by the user.
       */
      uint256 data;
    }
  
    struct EModeCategory {
      // each eMode category has a custom ltv and liquidation threshold
      uint16 ltv;
      uint16 liquidationThreshold;
      uint16 liquidationBonus;
      // each eMode category may or may not have a custom oracle to override the individual assets price oracles
      address priceSource;
      string label;
    }
  
    enum InterestRateMode {
      NONE,
      STABLE,
      VARIABLE
    }
  
    struct ReserveCache {
      uint256 currScaledVariableDebt;
      uint256 nextScaledVariableDebt;
      uint256 currPrincipalStableDebt;
      uint256 currAvgStableBorrowRate;
      uint256 currTotalStableDebt;
      uint256 nextAvgStableBorrowRate;
      uint256 nextTotalStableDebt;
      uint256 currLiquidityIndex;
      uint256 nextLiquidityIndex;
      uint256 currVariableBorrowIndex;
      uint256 nextVariableBorrowIndex;
      uint256 currLiquidityRate;
      uint256 currVariableBorrowRate;
      uint256 reserveFactor;
      ReserveConfigurationMap reserveConfiguration;
      address aTokenAddress;
      address stableDebtTokenAddress;
      address variableDebtTokenAddress;
      uint40 reserveLastUpdateTimestamp;
      uint40 stableDebtLastUpdateTimestamp;
    }
  
    struct ExecuteLiquidationCallParams {
      uint256 reservesCount;
      uint256 debtToCover;
      address collateralAsset;
      address debtAsset;
      address user;
      bool receiveAToken;
      address priceOracle;
      uint8 userEModeCategory;
      address priceOracleSentinel;
    }
  
    struct ExecuteSupplyParams {
      address asset;
      uint256 amount;
      address onBehalfOf;
      uint16 referralCode;
    }
  
    struct ExecuteBorrowParams {
      address asset;
      address user;
      address onBehalfOf;
      uint256 amount;
      InterestRateMode interestRateMode;
      uint16 referralCode;
      bool releaseUnderlying;
      uint256 maxStableRateBorrowSizePercent;
      uint256 reservesCount;
      address oracle;
      uint8 userEModeCategory;
      address priceOracleSentinel;
    }
  
    struct ExecuteRepayParams {
      address asset;
      uint256 amount;
      InterestRateMode interestRateMode;
      address onBehalfOf;
      bool useATokens;
    }
  
    struct ExecuteWithdrawParams {
      address asset;
      uint256 amount;
      address to;
      uint256 reservesCount;
      address oracle;
      uint8 userEModeCategory;
    }
  
    struct ExecuteSetUserEModeParams {
      uint256 reservesCount;
      address oracle;
      uint8 categoryId;
    }
  
    struct FinalizeTransferParams {
      address asset;
      address from;
      address to;
      uint256 amount;
      uint256 balanceFromBefore;
      uint256 balanceToBefore;
      uint256 reservesCount;
      address oracle;
      uint8 fromEModeCategory;
    }
  
    struct FlashloanParams {
      address receiverAddress;
      address[] assets;
      uint256[] amounts;
      uint256[] interestRateModes;
      address onBehalfOf;
      bytes params;
      uint16 referralCode;
      uint256 flashLoanPremiumToProtocol;
      uint256 flashLoanPremiumTotal;
      uint256 maxStableRateBorrowSizePercent;
      uint256 reservesCount;
      address addressesProvider;
      uint8 userEModeCategory;
      bool isAuthorizedFlashBorrower;
    }
  
    struct FlashloanSimpleParams {
      address receiverAddress;
      address asset;
      uint256 amount;
      bytes params;
      uint16 referralCode;
      uint256 flashLoanPremiumToProtocol;
      uint256 flashLoanPremiumTotal;
    }
  
    struct FlashLoanRepaymentParams {
      uint256 amount;
      uint256 totalPremium;
      uint256 flashLoanPremiumToProtocol;
      address asset;
      address receiverAddress;
      uint16 referralCode;
    }
  
    struct CalculateUserAccountDataParams {
      UserConfigurationMap userConfig;
      uint256 reservesCount;
      address user;
      address oracle;
      uint8 userEModeCategory;
    }
  
    struct ValidateBorrowParams {
      ReserveCache reserveCache;
      UserConfigurationMap userConfig;
      address asset;
      address userAddress;
      uint256 amount;
      InterestRateMode interestRateMode;
      uint256 maxStableLoanPercent;
      uint256 reservesCount;
      address oracle;
      uint8 userEModeCategory;
      address priceOracleSentinel;
      bool isolationModeActive;
      address isolationModeCollateralAddress;
      uint256 isolationModeDebtCeiling;
    }
  
    struct ValidateLiquidationCallParams {
      ReserveCache debtReserveCache;
      uint256 totalDebt;
      uint256 healthFactor;
      address priceOracleSentinel;
    }
  
    struct CalculateInterestRatesParams {
      uint256 unbacked;
      uint256 liquidityAdded;
      uint256 liquidityTaken;
      uint256 totalStableDebt;
      uint256 totalVariableDebt;
      uint256 averageStableBorrowRate;
      uint256 reserveFactor;
      address reserve;
      address aToken;
    }
  
    struct InitReserveParams {
      address asset;
      address aTokenAddress;
      address stableDebtAddress;
      address variableDebtAddress;
      address interestRateStrategyAddress;
      uint16 reservesCount;
      uint16 maxNumberReserves;
    }
  }
  
  
  
  
  
  
  interface IPoolAddressesProvider {
    /**
     * @dev Emitted when the market identifier is updated.
     * @param oldMarketId The old id of the market
     * @param newMarketId The new id of the market
     */
    event MarketIdSet(string indexed oldMarketId, string indexed newMarketId);
  
    /**
     * @dev Emitted when the pool is updated.
     * @param oldAddress The old address of the Pool
     * @param newAddress The new address of the Pool
     */
    event PoolUpdated(address indexed oldAddress, address indexed newAddress);
  
    /**
     * @dev Emitted when the pool configurator is updated.
     * @param oldAddress The old address of the PoolConfigurator
     * @param newAddress The new address of the PoolConfigurator
     */
    event PoolConfiguratorUpdated(address indexed oldAddress, address indexed newAddress);
  
    /**
     * @dev Emitted when the price oracle is updated.
     * @param oldAddress The old address of the PriceOracle
     * @param newAddress The new address of the PriceOracle
     */
    event PriceOracleUpdated(address indexed oldAddress, address indexed newAddress);
  
    /**
     * @dev Emitted when the ACL manager is updated.
     * @param oldAddress The old address of the ACLManager
     * @param newAddress The new address of the ACLManager
     */
    event ACLManagerUpdated(address indexed oldAddress, address indexed newAddress);
  
    /**
     * @dev Emitted when the ACL admin is updated.
     * @param oldAddress The old address of the ACLAdmin
     * @param newAddress The new address of the ACLAdmin
     */
    event ACLAdminUpdated(address indexed oldAddress, address indexed newAddress);
  
    /**
     * @dev Emitted when the price oracle sentinel is updated.
     * @param oldAddress The old address of the PriceOracleSentinel
     * @param newAddress The new address of the PriceOracleSentinel
     */
    event PriceOracleSentinelUpdated(address indexed oldAddress, address indexed newAddress);
  
    /**
     * @dev Emitted when the pool data provider is updated.
     * @param oldAddress The old address of the PoolDataProvider
     * @param newAddress The new address of the PoolDataProvider
     */
    event PoolDataProviderUpdated(address indexed oldAddress, address indexed newAddress);
  
    /**
     * @dev Emitted when a new proxy is created.
     * @param id The identifier of the proxy
     * @param proxyAddress The address of the created proxy contract
     * @param implementationAddress The address of the implementation contract
     */
    event ProxyCreated(
      bytes32 indexed id,
      address indexed proxyAddress,
      address indexed implementationAddress
    );
  
    /**
     * @dev Emitted when a new non-proxied contract address is registered.
     * @param id The identifier of the contract
     * @param oldAddress The address of the old contract
     * @param newAddress The address of the new contract
     */
    event AddressSet(bytes32 indexed id, address indexed oldAddress, address indexed newAddress);
  
    /**
     * @dev Emitted when the implementation of the proxy registered with id is updated
     * @param id The identifier of the contract
     * @param proxyAddress The address of the proxy contract
     * @param oldImplementationAddress The address of the old implementation contract
     * @param newImplementationAddress The address of the new implementation contract
     */
    event AddressSetAsProxy(
      bytes32 indexed id,
      address indexed proxyAddress,
      address oldImplementationAddress,
      address indexed newImplementationAddress
    );
  
    /**
     * @notice Returns the id of the Aave market to which this contract points to.
     * @return The market id
     **/
    function getMarketId() external view returns (string memory);
  
    /**
     * @notice Associates an id with a specific PoolAddressesProvider.
     * @dev This can be used to create an onchain registry of PoolAddressesProviders to
     * identify and validate multiple Aave markets.
     * @param newMarketId The market id
     */
    function setMarketId(string calldata newMarketId) external;
  
    /**
     * @notice Returns an address by its identifier.
     * @dev The returned address might be an EOA or a contract, potentially proxied
     * @dev It returns ZERO if there is no registered address with the given id
     * @param id The id
     * @return The address of the registered for the specified id
     */
    function getAddress(bytes32 id) external view returns (address);
  
    /**
     * @notice General function to update the implementation of a proxy registered with
     * certain `id`. If there is no proxy registered, it will instantiate one and
     * set as implementation the `newImplementationAddress`.
     * @dev IMPORTANT Use this function carefully, only for ids that don't have an explicit
     * setter function, in order to avoid unexpected consequences
     * @param id The id
     * @param newImplementationAddress The address of the new implementation
     */
    function setAddressAsProxy(bytes32 id, address newImplementationAddress) external;
  
    /**
     * @notice Sets an address for an id replacing the address saved in the addresses map.
     * @dev IMPORTANT Use this function carefully, as it will do a hard replacement
     * @param id The id
     * @param newAddress The address to set
     */
    function setAddress(bytes32 id, address newAddress) external;
  
    /**
     * @notice Returns the address of the Pool proxy.
     * @return The Pool proxy address
     **/
    function getPool() external view returns (address);
  
    /**
     * @notice Updates the implementation of the Pool, or creates a proxy
     * setting the new `pool` implementation when the function is called for the first time.
     * @param newPoolImpl The new Pool implementation
     **/
    function setPoolImpl(address newPoolImpl) external;
  
    /**
     * @notice Returns the address of the PoolConfigurator proxy.
     * @return The PoolConfigurator proxy address
     **/
    function getPoolConfigurator() external view returns (address);
  
    /**
     * @notice Updates the implementation of the PoolConfigurator, or creates a proxy
     * setting the new `PoolConfigurator` implementation when the function is called for the first time.
     * @param newPoolConfiguratorImpl The new PoolConfigurator implementation
     **/
    function setPoolConfiguratorImpl(address newPoolConfiguratorImpl) external;
  
    /**
     * @notice Returns the address of the price oracle.
     * @return The address of the PriceOracle
     */
    function getPriceOracle() external view returns (address);
  
    /**
     * @notice Updates the address of the price oracle.
     * @param newPriceOracle The address of the new PriceOracle
     */
    function setPriceOracle(address newPriceOracle) external;
  
    /**
     * @notice Returns the address of the ACL manager.
     * @return The address of the ACLManager
     */
    function getACLManager() external view returns (address);
  
    /**
     * @notice Updates the address of the ACL manager.
     * @param newAclManager The address of the new ACLManager
     **/
    function setACLManager(address newAclManager) external;
  
    /**
     * @notice Returns the address of the ACL admin.
     * @return The address of the ACL admin
     */
    function getACLAdmin() external view returns (address);
  
    /**
     * @notice Updates the address of the ACL admin.
     * @param newAclAdmin The address of the new ACL admin
     */
    function setACLAdmin(address newAclAdmin) external;
  
    /**
     * @notice Returns the address of the price oracle sentinel.
     * @return The address of the PriceOracleSentinel
     */
    function getPriceOracleSentinel() external view returns (address);
  
    /**
     * @notice Updates the address of the price oracle sentinel.
     * @param newPriceOracleSentinel The address of the new PriceOracleSentinel
     **/
    function setPriceOracleSentinel(address newPriceOracleSentinel) external;
  
    /**
     * @notice Returns the address of the data provider.
     * @return The address of the DataProvider
     */
    function getPoolDataProvider() external view returns (address);
  
    /**
     * @notice Updates the address of the data provider.
     * @param newDataProvider The address of the new DataProvider
     **/
    function setPoolDataProvider(address newDataProvider) external;
  }
  
  
  
  
  
  
  
  interface IPoolV3 {
    /**
     * @dev Emitted on mintUnbacked()
     * @param reserve The address of the underlying asset of the reserve
     * @param user The address initiating the supply
     * @param onBehalfOf The beneficiary of the supplied assets, receiving the aTokens
     * @param amount The amount of supplied assets
     * @param referralCode The referral code used
     **/
    event MintUnbacked(
      address indexed reserve,
      address user,
      address indexed onBehalfOf,
      uint256 amount,
      uint16 indexed referralCode
    );
  
    /**
     * @dev Emitted on backUnbacked()
     * @param reserve The address of the underlying asset of the reserve
     * @param backer The address paying for the backing
     * @param amount The amount added as backing
     * @param fee The amount paid in fees
     **/
    event BackUnbacked(address indexed reserve, address indexed backer, uint256 amount, uint256 fee);
  
    /**
     * @dev Emitted on supply()
     * @param reserve The address of the underlying asset of the reserve
     * @param user The address initiating the supply
     * @param onBehalfOf The beneficiary of the supply, receiving the aTokens
     * @param amount The amount supplied
     * @param referralCode The referral code used
     **/
    event Supply(
      address indexed reserve,
      address user,
      address indexed onBehalfOf,
      uint256 amount,
      uint16 indexed referralCode
    );
  
    /**
     * @dev Emitted on withdraw()
     * @param reserve The address of the underlying asset being withdrawn
     * @param user The address initiating the withdrawal, owner of aTokens
     * @param to The address that will receive the underlying
     * @param amount The amount to be withdrawn
     **/
    event Withdraw(address indexed reserve, address indexed user, address indexed to, uint256 amount);
  
    /**
     * @dev Emitted on borrow() and flashLoan() when debt needs to be opened
     * @param reserve The address of the underlying asset being borrowed
     * @param user The address of the user initiating the borrow(), receiving the funds on borrow() or just
     * initiator of the transaction on flashLoan()
     * @param onBehalfOf The address that will be getting the debt
     * @param amount The amount borrowed out
     * @param interestRateMode The rate mode: 1 for Stable, 2 for Variable
     * @param borrowRate The numeric rate at which the user has borrowed, expressed in ray
     * @param referralCode The referral code used
     **/
    event Borrow(
      address indexed reserve,
      address user,
      address indexed onBehalfOf,
      uint256 amount,
      DataTypes.InterestRateMode interestRateMode,
      uint256 borrowRate,
      uint16 indexed referralCode
    );
  
    /**
     * @dev Emitted on repay()
     * @param reserve The address of the underlying asset of the reserve
     * @param user The beneficiary of the repayment, getting his debt reduced
     * @param repayer The address of the user initiating the repay(), providing the funds
     * @param amount The amount repaid
     * @param useATokens True if the repayment is done using aTokens, `false` if done with underlying asset directly
     **/
    event Repay(
      address indexed reserve,
      address indexed user,
      address indexed repayer,
      uint256 amount,
      bool useATokens
    );
  
    /**
     * @dev Emitted on swapBorrowRateMode()
     * @param reserve The address of the underlying asset of the reserve
     * @param user The address of the user swapping his rate mode
     * @param interestRateMode The current interest rate mode of the position being swapped: 1 for Stable, 2 for Variable
     **/
    event SwapBorrowRateMode(
      address indexed reserve,
      address indexed user,
      DataTypes.InterestRateMode interestRateMode
    );
  
    /**
     * @dev Emitted on borrow(), repay() and liquidationCall() when using isolated assets
     * @param asset The address of the underlying asset of the reserve
     * @param totalDebt The total isolation mode debt for the reserve
     */
    event IsolationModeTotalDebtUpdated(address indexed asset, uint256 totalDebt);
  
    /**
     * @dev Emitted when the user selects a certain asset category for eMode
     * @param user The address of the user
     * @param categoryId The category id
     **/
    event UserEModeSet(address indexed user, uint8 categoryId);
  
    /**
     * @dev Emitted on setUserUseReserveAsCollateral()
     * @param reserve The address of the underlying asset of the reserve
     * @param user The address of the user enabling the usage as collateral
     **/
    event ReserveUsedAsCollateralEnabled(address indexed reserve, address indexed user);
  
    /**
     * @dev Emitted on setUserUseReserveAsCollateral()
     * @param reserve The address of the underlying asset of the reserve
     * @param user The address of the user enabling the usage as collateral
     **/
    event ReserveUsedAsCollateralDisabled(address indexed reserve, address indexed user);
  
    /**
     * @dev Emitted on rebalanceStableBorrowRate()
     * @param reserve The address of the underlying asset of the reserve
     * @param user The address of the user for which the rebalance has been executed
     **/
    event RebalanceStableBorrowRate(address indexed reserve, address indexed user);
  
    /**
     * @dev Emitted on flashLoan()
     * @param target The address of the flash loan receiver contract
     * @param initiator The address initiating the flash loan
     * @param asset The address of the asset being flash borrowed
     * @param amount The amount flash borrowed
     * @param interestRateMode The flashloan mode: 0 for regular flashloan, 1 for Stable debt, 2 for Variable debt
     * @param premium The fee flash borrowed
     * @param referralCode The referral code used
     **/
    event FlashLoan(
      address indexed target,
      address initiator,
      address indexed asset,
      uint256 amount,
      DataTypes.InterestRateMode interestRateMode,
      uint256 premium,
      uint16 indexed referralCode
    );
  
    /**
     * @dev Emitted when a borrower is liquidated.
     * @param collateralAsset The address of the underlying asset used as collateral, to receive as result of the liquidation
     * @param debtAsset The address of the underlying borrowed asset to be repaid with the liquidation
     * @param user The address of the borrower getting liquidated
     * @param debtToCover The debt amount of borrowed `asset` the liquidator wants to cover
     * @param liquidatedCollateralAmount The amount of collateral received by the liquidator
     * @param liquidator The address of the liquidator
     * @param receiveAToken True if the liquidators wants to receive the collateral aTokens, `false` if he wants
     * to receive the underlying collateral asset directly
     **/
    event LiquidationCall(
      address indexed collateralAsset,
      address indexed debtAsset,
      address indexed user,
      uint256 debtToCover,
      uint256 liquidatedCollateralAmount,
      address liquidator,
      bool receiveAToken
    );
  
    /**
     * @dev Emitted when the state of a reserve is updated.
     * @param reserve The address of the underlying asset of the reserve
     * @param liquidityRate The next liquidity rate
     * @param stableBorrowRate The next stable borrow rate
     * @param variableBorrowRate The next variable borrow rate
     * @param liquidityIndex The next liquidity index
     * @param variableBorrowIndex The next variable borrow index
     **/
    event ReserveDataUpdated(
      address indexed reserve,
      uint256 liquidityRate,
      uint256 stableBorrowRate,
      uint256 variableBorrowRate,
      uint256 liquidityIndex,
      uint256 variableBorrowIndex
    );
  
    /**
     * @dev Emitted when the protocol treasury receives minted aTokens from the accrued interest.
     * @param reserve The address of the reserve
     * @param amountMinted The amount minted to the treasury
     **/
    event MintedToTreasury(address indexed reserve, uint256 amountMinted);
  
    /**
     * @dev Mints an `amount` of aTokens to the `onBehalfOf`
     * @param asset The address of the underlying asset to mint
     * @param amount The amount to mint
     * @param onBehalfOf The address that will receive the aTokens
     * @param referralCode Code used to register the integrator originating the operation, for potential rewards.
     *   0 if the action is executed directly by the user, without any middle-man
     **/
    function mintUnbacked(
      address asset,
      uint256 amount,
      address onBehalfOf,
      uint16 referralCode
    ) external;
  
    /**
     * @dev Back the current unbacked underlying with `amount` and pay `fee`.
     * @param asset The address of the underlying asset to back
     * @param amount The amount to back
     * @param fee The amount paid in fees
     **/
    function backUnbacked(
      address asset,
      uint256 amount,
      uint256 fee
    ) external;
  
    /**
     * @notice Supplies an `amount` of underlying asset into the reserve, receiving in return overlying aTokens.
     * - E.g. User supplies 100 USDC and gets in return 100 aUSDC
     * @param asset The address of the underlying asset to supply
     * @param amount The amount to be supplied
     * @param onBehalfOf The address that will receive the aTokens, same as msg.sender if the user
     *   wants to receive them on his own wallet, or a different address if the beneficiary of aTokens
     *   is a different wallet
     * @param referralCode Code used to register the integrator originating the operation, for potential rewards.
     *   0 if the action is executed directly by the user, without any middle-man
     **/
    function supply(
      address asset,
      uint256 amount,
      address onBehalfOf,
      uint16 referralCode
    ) external;
  
    /**
     * @notice Supply with transfer approval of asset to be supplied done via permit function
     * see: https://eips.ethereum.org/EIPS/eip-2612 and https://eips.ethereum.org/EIPS/eip-713
     * @param asset The address of the underlying asset to supply
     * @param amount The amount to be supplied
     * @param onBehalfOf The address that will receive the aTokens, same as msg.sender if the user
     *   wants to receive them on his own wallet, or a different address if the beneficiary of aTokens
     *   is a different wallet
     * @param deadline The deadline timestamp that the permit is valid
     * @param referralCode Code used to register the integrator originating the operation, for potential rewards.
     *   0 if the action is executed directly by the user, without any middle-man
     * @param permitV The V parameter of ERC712 permit sig
     * @param permitR The R parameter of ERC712 permit sig
     * @param permitS The S parameter of ERC712 permit sig
     **/
    function supplyWithPermit(
      address asset,
      uint256 amount,
      address onBehalfOf,
      uint16 referralCode,
      uint256 deadline,
      uint8 permitV,
      bytes32 permitR,
      bytes32 permitS
    ) external;
  
    /**
     * @notice Withdraws an `amount` of underlying asset from the reserve, burning the equivalent aTokens owned
     * E.g. User has 100 aUSDC, calls withdraw() and receives 100 USDC, burning the 100 aUSDC
     * @param asset The address of the underlying asset to withdraw
     * @param amount The underlying amount to be withdrawn
     *   - Send the value type(uint256).max in order to withdraw the whole aToken balance
     * @param to The address that will receive the underlying, same as msg.sender if the user
     *   wants to receive it on his own wallet, or a different address if the beneficiary is a
     *   different wallet
     * @return The final amount withdrawn
     **/
    function withdraw(
      address asset,
      uint256 amount,
      address to
    ) external returns (uint256);
  
    /**
     * @notice Allows users to borrow a specific `amount` of the reserve underlying asset, provided that the borrower
     * already supplied enough collateral, or he was given enough allowance by a credit delegator on the
     * corresponding debt token (StableDebtToken or VariableDebtToken)
     * - E.g. User borrows 100 USDC passing as `onBehalfOf` his own address, receiving the 100 USDC in his wallet
     *   and 100 stable/variable debt tokens, depending on the `interestRateMode`
     * @param asset The address of the underlying asset to borrow
     * @param amount The amount to be borrowed
     * @param interestRateMode The interest rate mode at which the user wants to borrow: 1 for Stable, 2 for Variable
     * @param referralCode The code used to register the integrator originating the operation, for potential rewards.
     *   0 if the action is executed directly by the user, without any middle-man
     * @param onBehalfOf The address of the user who will receive the debt. Should be the address of the borrower itself
     * calling the function if he wants to borrow against his own collateral, or the address of the credit delegator
     * if he has been given credit delegation allowance
     **/
    function borrow(
      address asset,
      uint256 amount,
      uint256 interestRateMode,
      uint16 referralCode,
      address onBehalfOf
    ) external;
  
    /**
     * @notice Repays a borrowed `amount` on a specific reserve, burning the equivalent debt tokens owned
     * - E.g. User repays 100 USDC, burning 100 variable/stable debt tokens of the `onBehalfOf` address
     * @param asset The address of the borrowed underlying asset previously borrowed
     * @param amount The amount to repay
     * - Send the value type(uint256).max in order to repay the whole debt for `asset` on the specific `debtMode`
     * @param interestRateMode The interest rate mode at of the debt the user wants to repay: 1 for Stable, 2 for Variable
     * @param onBehalfOf The address of the user who will get his debt reduced/removed. Should be the address of the
     * user calling the function if he wants to reduce/remove his own debt, or the address of any other
     * other borrower whose debt should be removed
     * @return The final amount repaid
     **/
    function repay(
      address asset,
      uint256 amount,
      uint256 interestRateMode,
      address onBehalfOf
    ) external returns (uint256);
  
    /**
     * @notice Repay with transfer approval of asset to be repaid done via permit function
     * see: https://eips.ethereum.org/EIPS/eip-2612 and https://eips.ethereum.org/EIPS/eip-713
     * @param asset The address of the borrowed underlying asset previously borrowed
     * @param amount The amount to repay
     * - Send the value type(uint256).max in order to repay the whole debt for `asset` on the specific `debtMode`
     * @param interestRateMode The interest rate mode at of the debt the user wants to repay: 1 for Stable, 2 for Variable
     * @param onBehalfOf Address of the user who will get his debt reduced/removed. Should be the address of the
     * user calling the function if he wants to reduce/remove his own debt, or the address of any other
     * other borrower whose debt should be removed
     * @param deadline The deadline timestamp that the permit is valid
     * @param permitV The V parameter of ERC712 permit sig
     * @param permitR The R parameter of ERC712 permit sig
     * @param permitS The S parameter of ERC712 permit sig
     * @return The final amount repaid
     **/
    function repayWithPermit(
      address asset,
      uint256 amount,
      uint256 interestRateMode,
      address onBehalfOf,
      uint256 deadline,
      uint8 permitV,
      bytes32 permitR,
      bytes32 permitS
    ) external returns (uint256);
  
    /**
     * @notice Repays a borrowed `amount` on a specific reserve using the reserve aTokens, burning the
     * equivalent debt tokens
     * - E.g. User repays 100 USDC using 100 aUSDC, burning 100 variable/stable debt tokens
     * @dev  Passing uint256.max as amount will clean up any residual aToken dust balance, if the user aToken
     * balance is not enough to cover the whole debt
     * @param asset The address of the borrowed underlying asset previously borrowed
     * @param amount The amount to repay
     * - Send the value type(uint256).max in order to repay the whole debt for `asset` on the specific `debtMode`
     * @param interestRateMode The interest rate mode at of the debt the user wants to repay: 1 for Stable, 2 for Variable
     * @return The final amount repaid
     **/
    function repayWithATokens(
      address asset,
      uint256 amount,
      uint256 interestRateMode
    ) external returns (uint256);
  
    /**
     * @notice Allows a borrower to swap his debt between stable and variable mode, or vice versa
     * @param asset The address of the underlying asset borrowed
     * @param interestRateMode The current interest rate mode of the position being swapped: 1 for Stable, 2 for Variable
     **/
    function swapBorrowRateMode(address asset, uint256 interestRateMode) external;
  
    /**
     * @notice Rebalances the stable interest rate of a user to the current stable rate defined on the reserve.
     * - Users can be rebalanced if the following conditions are satisfied:
     *     1. Usage ratio is above 95%
     *     2. the current supply APY is below REBALANCE_UP_THRESHOLD * maxVariableBorrowRate, which means that too
     *        much has been borrowed at a stable rate and suppliers are not earning enough
     * @param asset The address of the underlying asset borrowed
     * @param user The address of the user to be rebalanced
     **/
    function rebalanceStableBorrowRate(address asset, address user) external;
  
    /**
     * @notice Allows suppliers to enable/disable a specific supplied asset as collateral
     * @param asset The address of the underlying asset supplied
     * @param useAsCollateral True if the user wants to use the supply as collateral, false otherwise
     **/
    function setUserUseReserveAsCollateral(address asset, bool useAsCollateral) external;
  
    /**
     * @notice Function to liquidate a non-healthy position collateral-wise, with Health Factor below 1
     * - The caller (liquidator) covers `debtToCover` amount of debt of the user getting liquidated, and receives
     *   a proportionally amount of the `collateralAsset` plus a bonus to cover market risk
     * @param collateralAsset The address of the underlying asset used as collateral, to receive as result of the liquidation
     * @param debtAsset The address of the underlying borrowed asset to be repaid with the liquidation
     * @param user The address of the borrower getting liquidated
     * @param debtToCover The debt amount of borrowed `asset` the liquidator wants to cover
     * @param receiveAToken True if the liquidators wants to receive the collateral aTokens, `false` if he wants
     * to receive the underlying collateral asset directly
     **/
    function liquidationCall(
      address collateralAsset,
      address debtAsset,
      address user,
      uint256 debtToCover,
      bool receiveAToken
    ) external;
  
    /**
     * @notice Allows smartcontracts to access the liquidity of the pool within one transaction,
     * as long as the amount taken plus a fee is returned.
     * @dev IMPORTANT There are security concerns for developers of flashloan receiver contracts that must be kept
     * into consideration. For further details please visit https://developers.aave.com
     * @param receiverAddress The address of the contract receiving the funds, implementing IFlashLoanReceiver interface
     * @param assets The addresses of the assets being flash-borrowed
     * @param amounts The amounts of the assets being flash-borrowed
     * @param interestRateModes Types of the debt to open if the flash loan is not returned:
     *   0 -> Don't open any debt, just revert if funds can't be transferred from the receiver
     *   1 -> Open debt at stable rate for the value of the amount flash-borrowed to the `onBehalfOf` address
     *   2 -> Open debt at variable rate for the value of the amount flash-borrowed to the `onBehalfOf` address
     * @param onBehalfOf The address  that will receive the debt in the case of using on `modes` 1 or 2
     * @param params Variadic packed params to pass to the receiver as extra information
     * @param referralCode The code used to register the integrator originating the operation, for potential rewards.
     *   0 if the action is executed directly by the user, without any middle-man
     **/
    function flashLoan(
      address receiverAddress,
      address[] calldata assets,
      uint256[] calldata amounts,
      uint256[] calldata interestRateModes,
      address onBehalfOf,
      bytes calldata params,
      uint16 referralCode
    ) external;
  
    /**
     * @notice Allows smartcontracts to access the liquidity of the pool within one transaction,
     * as long as the amount taken plus a fee is returned.
     * @dev IMPORTANT There are security concerns for developers of flashloan receiver contracts that must be kept
     * into consideration. For further details please visit https://developers.aave.com
     * @param receiverAddress The address of the contract receiving the funds, implementing IFlashLoanSimpleReceiver interface
     * @param asset The address of the asset being flash-borrowed
     * @param amount The amount of the asset being flash-borrowed
     * @param params Variadic packed params to pass to the receiver as extra information
     * @param referralCode The code used to register the integrator originating the operation, for potential rewards.
     *   0 if the action is executed directly by the user, without any middle-man
     **/
    function flashLoanSimple(
      address receiverAddress,
      address asset,
      uint256 amount,
      bytes calldata params,
      uint16 referralCode
    ) external;
  
    /**
     * @notice Returns the user account data across all the reserves
     * @param user The address of the user
     * @return totalCollateralBase The total collateral of the user in the base currency used by the price feed
     * @return totalDebtBase The total debt of the user in the base currency used by the price feed
     * @return availableBorrowsBase The borrowing power left of the user in the base currency used by the price feed
     * @return currentLiquidationThreshold The liquidation threshold of the user
     * @return ltv The loan to value of The user
     * @return healthFactor The current health factor of the user
     **/
    function getUserAccountData(address user)
      external
      view
      returns (
        uint256 totalCollateralBase,
        uint256 totalDebtBase,
        uint256 availableBorrowsBase,
        uint256 currentLiquidationThreshold,
        uint256 ltv,
        uint256 healthFactor
      );
  
    /**
     * @notice Initializes a reserve, activating it, assigning an aToken and debt tokens and an
     * interest rate strategy
     * @dev Only callable by the PoolConfigurator contract
     * @param asset The address of the underlying asset of the reserve
     * @param aTokenAddress The address of the aToken that will be assigned to the reserve
     * @param stableDebtAddress The address of the StableDebtToken that will be assigned to the reserve
     * @param variableDebtAddress The address of the VariableDebtToken that will be assigned to the reserve
     * @param interestRateStrategyAddress The address of the interest rate strategy contract
     **/
    function initReserve(
      address asset,
      address aTokenAddress,
      address stableDebtAddress,
      address variableDebtAddress,
      address interestRateStrategyAddress
    ) external;
  
    /**
     * @notice Drop a reserve
     * @dev Only callable by the PoolConfigurator contract
     * @param asset The address of the underlying asset of the reserve
     **/
    function dropReserve(address asset) external;
  
    /**
     * @notice Updates the address of the interest rate strategy contract
     * @dev Only callable by the PoolConfigurator contract
     * @param asset The address of the underlying asset of the reserve
     * @param rateStrategyAddress The address of the interest rate strategy contract
     **/
    function setReserveInterestRateStrategyAddress(address asset, address rateStrategyAddress)
      external;
  
    /**
     * @notice Sets the configuration bitmap of the reserve as a whole
     * @dev Only callable by the PoolConfigurator contract
     * @param asset The address of the underlying asset of the reserve
     * @param configuration The new configuration bitmap
     **/
    function setConfiguration(address asset, DataTypes.ReserveConfigurationMap calldata configuration)
      external;
  
    /**
     * @notice Returns the configuration of the reserve
     * @param asset The address of the underlying asset of the reserve
     * @return The configuration of the reserve
     **/
    function getConfiguration(address asset)
      external
      view
      returns (DataTypes.ReserveConfigurationMap memory);
  
    /**
     * @notice Returns the configuration of the user across all the reserves
     * @param user The user address
     * @return The configuration of the user
     **/
    function getUserConfiguration(address user)
      external
      view
      returns (DataTypes.UserConfigurationMap memory);
  
    /**
     * @notice Returns the normalized income normalized income of the reserve
     * @param asset The address of the underlying asset of the reserve
     * @return The reserve's normalized income
     */
    function getReserveNormalizedIncome(address asset) external view returns (uint256);
  
    /**
     * @notice Returns the normalized variable debt per unit of asset
     * @param asset The address of the underlying asset of the reserve
     * @return The reserve normalized variable debt
     */
    function getReserveNormalizedVariableDebt(address asset) external view returns (uint256);
  
    /**
     * @notice Returns the state and configuration of the reserve
     * @param asset The address of the underlying asset of the reserve
     * @return The state and configuration data of the reserve
     **/
    function getReserveData(address asset) external view returns (DataTypes.ReserveData memory);
  
    /**
     * @notice Validates and finalizes an aToken transfer
     * @dev Only callable by the overlying aToken of the `asset`
     * @param asset The address of the underlying asset of the aToken
     * @param from The user from which the aTokens are transferred
     * @param to The user receiving the aTokens
     * @param amount The amount being transferred/withdrawn
     * @param balanceFromBefore The aToken balance of the `from` user before the transfer
     * @param balanceToBefore The aToken balance of the `to` user before the transfer
     */
    function finalizeTransfer(
      address asset,
      address from,
      address to,
      uint256 amount,
      uint256 balanceFromBefore,
      uint256 balanceToBefore
    ) external;
  
    /**
     * @notice Returns the list of the initialized reserves
     * @dev It does not include dropped reserves
     * @return The addresses of the reserves
     **/
    function getReservesList() external view returns (address[] memory);
  
    /**
     * @notice Returns the PoolAddressesProvider connected to this contract
     * @return The address of the PoolAddressesProvider
     **/
    function ADDRESSES_PROVIDER() external view returns (IPoolAddressesProvider);
  
    /**
     * @notice Updates the protocol fee on the bridging
     * @param bridgeProtocolFee The part of the premium sent to the protocol treasury
     */
    function updateBridgeProtocolFee(uint256 bridgeProtocolFee) external;
  
    /**
     * @notice Updates flash loan premiums. Flash loan premium consists of two parts:
     * - A part is sent to aToken holders as extra, one time accumulated interest
     * - A part is collected by the protocol treasury
     * @dev The total premium is calculated on the total borrowed amount
     * @dev The premium to protocol is calculated on the total premium, being a percentage of `flashLoanPremiumTotal`
     * @dev Only callable by the PoolConfigurator contract
     * @param flashLoanPremiumTotal The total premium, expressed in bps
     * @param flashLoanPremiumToProtocol The part of the premium sent to the protocol treasury, expressed in bps
     */
    function updateFlashloanPremiums(
      uint128 flashLoanPremiumTotal,
      uint128 flashLoanPremiumToProtocol
    ) external;
  
    /**
     * @notice Configures a new category for the eMode.
     * @dev In eMode, the protocol allows very high borrowing power to borrow assets of the same category.
     * The category 0 is reserved as it's the default for volatile assets
     * @param id The id of the category
     * @param config The configuration of the category
     */
    function configureEModeCategory(uint8 id, DataTypes.EModeCategory memory config) external;
  
    /**
     * @notice Returns the data of an eMode category
     * @param id The id of the category
     * @return The configuration data of the category
     */
    function getEModeCategoryData(uint8 id) external view returns (DataTypes.EModeCategory memory);
  
    /**
     * @notice Allows a user to use the protocol in eMode
     * @param categoryId The id of the category
     */
    function setUserEMode(uint8 categoryId) external;
  
    /**
     * @notice Returns the eMode the user is using
     * @param user The address of the user
     * @return The eMode id
     */
    function getUserEMode(address user) external view returns (uint256);
  
    /**
     * @notice Resets the isolation mode total debt of the given asset to zero
     * @dev It requires the given asset has zero debt ceiling
     * @param asset The address of the underlying asset to reset the isolationModeTotalDebt
     */
    function resetIsolationModeTotalDebt(address asset) external;
  
    /**
     * @notice Returns the percentage of available liquidity that can be borrowed at once at stable rate
     * @return The percentage of available liquidity to borrow, expressed in bps
     */
    function MAX_STABLE_RATE_BORROW_SIZE_PERCENT() external view returns (uint256);
  
    /**
     * @notice Returns the total fee on flash loans
     * @return The total fee on flashloans
     */
    function FLASHLOAN_PREMIUM_TOTAL() external view returns (uint128);
  
    /**
     * @notice Returns the part of the bridge fees sent to protocol
     * @return The bridge fee sent to the protocol treasury
     */
    function BRIDGE_PROTOCOL_FEE() external view returns (uint256);
  
    /**
     * @notice Returns the part of the flashloan fees sent to protocol
     * @return The flashloan fee sent to the protocol treasury
     */
    function FLASHLOAN_PREMIUM_TO_PROTOCOL() external view returns (uint128);
  
    /**
     * @notice Returns the maximum number of reserves supported to be listed in this Pool
     * @return The maximum number of reserves supported
     */
    function MAX_NUMBER_RESERVES() external view returns (uint16);
  
    /**
     * @notice Mints the assets accrued through the reserve factor to the treasury in the form of aTokens
     * @param assets The list of reserves for which the minting needs to be executed
     **/
    function mintToTreasury(address[] calldata assets) external;
  
    /**
     * @notice Rescue and transfer tokens locked in this contract
     * @param token The address of the token
     * @param to The address of the recipient
     * @param amount The amount of token to transfer
     */
    function rescueTokens(
      address token,
      address to,
      uint256 amount
    ) external;
  
    /**
     * @notice Supplies an `amount` of underlying asset into the reserve, receiving in return overlying aTokens.
     * - E.g. User supplies 100 USDC and gets in return 100 aUSDC
     * @dev Deprecated: Use the `supply` function instead
     * @param asset The address of the underlying asset to supply
     * @param amount The amount to be supplied
     * @param onBehalfOf The address that will receive the aTokens, same as msg.sender if the user
     *   wants to receive them on his own wallet, or a different address if the beneficiary of aTokens
     *   is a different wallet
     * @param referralCode Code used to register the integrator originating the operation, for potential rewards.
     *   0 if the action is executed directly by the user, without any middle-man
     **/
    function deposit(
      address asset,
      uint256 amount,
      address onBehalfOf,
      uint16 referralCode
    ) external;
  
    /**
     * @notice Returns the address of the underlying asset of a reserve by the reserve id as stored in the DataTypes.ReserveData struct
     * @param id The id of the reserve as stored in the DataTypes.ReserveData struct
     * @return The address of the reserve associated with id
     **/
    function getReserveAddressById(uint16 id) external view returns (address);
  
  }
  
  
  
  
  
  
  
  interface IL2PoolV3 is IPoolV3{
    /**
     * @notice Calldata efficient wrapper of the supply function on behalf of the caller
     * @param args Arguments for the supply function packed in one bytes32
     *    96 bits       16 bits         128 bits      16 bits
     * | 0-padding | referralCode | shortenedAmount | assetId |
     * @dev the shortenedAmount is cast to 256 bits at decode time, if type(uint128).max the value will be expanded to
     * type(uint256).max
     * @dev assetId is the index of the asset in the reservesList.
     */
    function supply(bytes32 args) external;
  
    /**
     * @notice Calldata efficient wrapper of the supplyWithPermit function on behalf of the caller
     * @param args Arguments for the supply function packed in one bytes32
     *    56 bits    8 bits         32 bits           16 bits         128 bits      16 bits
     * | 0-padding | permitV | shortenedDeadline | referralCode | shortenedAmount | assetId |
     * @dev the shortenedAmount is cast to 256 bits at decode time, if type(uint128).max the value will be expanded to
     * type(uint256).max
     * @dev assetId is the index of the asset in the reservesList.
     * @param r The R parameter of ERC712 permit sig
     * @param s The S parameter of ERC712 permit sig
     */
    function supplyWithPermit(
      bytes32 args,
      bytes32 r,
      bytes32 s
    ) external;
  
    /**
     * @notice Calldata efficient wrapper of the withdraw function, withdrawing to the caller
     * @param args Arguments for the withdraw function packed in one bytes32
     *    112 bits       128 bits      16 bits
     * | 0-padding | shortenedAmount | assetId |
     * @dev the shortenedAmount is cast to 256 bits at decode time, if type(uint128).max the value will be expanded to
     * type(uint256).max
     * @dev assetId is the index of the asset in the reservesList.
     */
    function withdraw(bytes32 args) external;
  
    /**
     * @notice Calldata efficient wrapper of the borrow function, borrowing on behalf of the caller
     * @param args Arguments for the borrow function packed in one bytes32
     *    88 bits       16 bits             8 bits                 128 bits       16 bits
     * | 0-padding | referralCode | shortenedInterestRateMode | shortenedAmount | assetId |
     * @dev the shortenedAmount is cast to 256 bits at decode time, if type(uint128).max the value will be expanded to
     * type(uint256).max
     * @dev assetId is the index of the asset in the reservesList.
     */
    function borrow(bytes32 args) external;
  
    /**
     * @notice Calldata efficient wrapper of the repay function, repaying on behalf of the caller
     * @param args Arguments for the repay function packed in one bytes32
     *    104 bits             8 bits               128 bits       16 bits
     * | 0-padding | shortenedInterestRateMode | shortenedAmount | assetId |
     * @dev the shortenedAmount is cast to 256 bits at decode time, if type(uint128).max the value will be expanded to
     * type(uint256).max
     * @dev assetId is the index of the asset in the reservesList.
     * @return The final amount repaid
     */
    function repay(bytes32 args) external returns (uint256);
  
    /**
     * @notice Calldata efficient wrapper of the repayWithPermit function, repaying on behalf of the caller
     * @param args Arguments for the repayWithPermit function packed in one bytes32
     *    64 bits    8 bits        32 bits                   8 bits               128 bits       16 bits
     * | 0-padding | permitV | shortenedDeadline | shortenedInterestRateMode | shortenedAmount | assetId |
     * @dev the shortenedAmount is cast to 256 bits at decode time, if type(uint128).max the value will be expanded to
     * type(uint256).max
     * @dev assetId is the index of the asset in the reservesList.
     * @param r The R parameter of ERC712 permit sig
     * @param s The S parameter of ERC712 permit sig
     * @return The final amount repaid
     */
    function repayWithPermit(
      bytes32 args,
      bytes32 r,
      bytes32 s
    ) external returns (uint256);
  
    /**
     * @notice Calldata efficient wrapper of the repayWithATokens function
     * @param args Arguments for the repayWithATokens function packed in one bytes32
     *    104 bits             8 bits               128 bits       16 bits
     * | 0-padding | shortenedInterestRateMode | shortenedAmount | assetId |
     * @dev the shortenedAmount is cast to 256 bits at decode time, if type(uint128).max the value will be expanded to
     * type(uint256).max
     * @dev assetId is the index of the asset in the reservesList.
     * @return The final amount repaid
     */
    function repayWithATokens(bytes32 args) external returns (uint256);
  
    /**
     * @notice Calldata efficient wrapper of the swapBorrowRateMode function
     * @param args Arguments for the swapBorrowRateMode function packed in one bytes32
     *    232 bits            8 bits             16 bits
     * | 0-padding | shortenedInterestRateMode | assetId |
     * @dev assetId is the index of the asset in the reservesList.
     */
    function swapBorrowRateMode(bytes32 args) external;
  
    /**
     * @notice Calldata efficient wrapper of the rebalanceStableBorrowRate function
     * @param args Arguments for the rebalanceStableBorrowRate function packed in one bytes32
     *    80 bits      160 bits     16 bits
     * | 0-padding | user address | assetId |
     * @dev assetId is the index of the asset in the reservesList.
     */
    function rebalanceStableBorrowRate(bytes32 args) external;
  
    /**
     * @notice Calldata efficient wrapper of the setUserUseReserveAsCollateral function
     * @param args Arguments for the setUserUseReserveAsCollateral function packed in one bytes32
     *    239 bits         1 bit       16 bits
     * | 0-padding | useAsCollateral | assetId |
     * @dev assetId is the index of the asset in the reservesList.
     */
    function setUserUseReserveAsCollateral(bytes32 args) external;
  
    /**
     * @notice Calldata efficient wrapper of the liquidationCall function
     * @param args1 part of the arguments for the liquidationCall function packed in one bytes32
     *    64 bits      160 bits       16 bits         16 bits
     * | 0-padding | user address | debtAssetId | collateralAssetId |
     * @param args2 part of the arguments for the liquidationCall function packed in one bytes32
     *    127 bits       1 bit             128 bits
     * | 0-padding | receiveAToken | shortenedDebtToCover |
     * @dev the shortenedDebtToCover is cast to 256 bits at decode time,
     * if type(uint128).max the value will be expanded to type(uint256).max
     */
    function liquidationCall(bytes32 args1, bytes32 args2) external;
  }
  
  
  
  
  
  
  
  
  
  contract AaveV3Helper is MainnetAaveV3Addresses {
      
      uint16 public constant AAVE_REFERRAL_CODE = 64;
  
      
      
      /// @notice Returns the lending pool contract of the specified market
      function getLendingPool(address _market) internal virtual view returns (IL2PoolV3) {
          return IL2PoolV3(IPoolAddressesProvider(_market).getPool());
      }
  
      /// @notice Fetch the data provider for the specified market
      function getDataProvider(address _market) internal virtual view returns (IAaveProtocolDataProvider) {
          return
              IAaveProtocolDataProvider(
                  IPoolAddressesProvider(_market).getPoolDataProvider()
              );
      }
  
      function boolToBytes(bool x) internal virtual pure returns (bytes1 r) {
         return x ? bytes1(0x01) : bytes1(0x00);
      }
  
      function bytesToBool(bytes1 x) internal virtual pure returns (bool r) {
          return x != bytes1(0x00);
      }
      
      function getWholeDebt(address _market, address _tokenAddr, uint _borrowType, address _debtOwner) internal virtual view returns (uint256 debt) {
          uint256 STABLE_ID = 1;
          uint256 VARIABLE_ID = 2;
  
          IAaveProtocolDataProvider dataProvider = getDataProvider(_market);
          (, uint256 borrowsStable, uint256 borrowsVariable, , , , , , ) =
              dataProvider.getUserReserveData(_tokenAddr, _debtOwner);
  
          if (_borrowType == STABLE_ID) {
              debt = borrowsStable;
          } else if (_borrowType == VARIABLE_ID) {
              debt = borrowsVariable;
          }
      }
  }
  
  
  
  
  
  
  
  contract MainnetActionsUtilAddresses {
      address internal constant DFS_REG_CONTROLLER_ADDR = 0xF8f8B3C98Cf2E63Df3041b73f80F362a4cf3A576;
      address internal constant REGISTRY_ADDR = 0x287778F121F134C66212FB16c9b53eC991D32f5b;
      address internal constant DFS_LOGGER_ADDR = 0xcE7a977Cac4a481bc84AC06b2Da0df614e621cf3;
      address internal constant SUB_STORAGE_ADDR = 0x1612fc28Ee0AB882eC99842Cde0Fc77ff0691e90;
      address internal constant PROXY_AUTH_ADDR = 0x149667b6FAe2c63D1B4317C716b0D0e4d3E2bD70;
      address internal constant LSV_PROXY_REGISTRY_ADDRESS = 0xa8a3c86c4A2DcCf350E84D2b3c46BDeBc711C16e;
      address internal constant TRANSIENT_STORAGE = 0x2F7Ef2ea5E8c97B8687CA703A0e50Aa5a49B7eb2;
  }
  
  
  
  
  
  
  
  contract ActionsUtilHelper is MainnetActionsUtilAddresses {
  }
  
  
  
  
  
  
  
  contract MainnetAuthAddresses {
      address internal constant ADMIN_VAULT_ADDR = 0xCCf3d848e08b94478Ed8f46fFead3008faF581fD;
      address internal constant DSGUARD_FACTORY_ADDRESS = 0x5a15566417e6C1c9546523066500bDDBc53F88C7;
      address internal constant ADMIN_ADDR = 0x25eFA336886C74eA8E282ac466BdCd0199f85BB9; // USED IN ADMIN VAULT CONSTRUCTOR
      address internal constant PROXY_AUTH_ADDRESS = 0x149667b6FAe2c63D1B4317C716b0D0e4d3E2bD70;
      address internal constant MODULE_AUTH_ADDRESS = 0x7407974DDBF539e552F1d051e44573090912CC3D;
  }
  
  
  
  
  
  
  
  contract AuthHelper is MainnetAuthAddresses {
  }
  
  
  
  
  
  
  
  
  contract AdminVault is AuthHelper {
      address public owner;
      address public admin;
  
      error SenderNotAdmin();
  
      constructor() {
          owner = msg.sender;
          admin = ADMIN_ADDR;
      }
  
      /// @notice Admin is able to change owner
      /// @param _owner Address of new owner
      function changeOwner(address _owner) public {
          if (admin != msg.sender){
              revert SenderNotAdmin();
          }
          owner = _owner;
      }
  
      /// @notice Admin is able to set new admin
      /// @param _admin Address of multisig that becomes new admin
      function changeAdmin(address _admin) public {
          if (admin != msg.sender){
              revert SenderNotAdmin();
          }
          admin = _admin;
      }
  
  }
  
  
  
  
  
  
  
  interface IERC20 {
      function name() external view returns (string memory);
      function symbol() external view returns (string memory);
      function decimals() external view returns (uint256 digits);
      function totalSupply() external view returns (uint256 supply);
  
      function balanceOf(address _owner) external view returns (uint256 balance);
  
      function transfer(address _to, uint256 _value) external returns (bool success);
  
      function transferFrom(
          address _from,
          address _to,
          uint256 _value
      ) external returns (bool success);
  
      function approve(address _spender, uint256 _value) external returns (bool success);
  
      function allowance(address _owner, address _spender) external view returns (uint256 remaining);
  
      event Approval(address indexed _owner, address indexed _spender, uint256 _value);
  }
  
  
  
  
  
  
  
  library Address {
      //insufficient balance
      error InsufficientBalance(uint256 available, uint256 required);
      //unable to send value, recipient may have reverted
      error SendingValueFail();
      //insufficient balance for call
      error InsufficientBalanceForCall(uint256 available, uint256 required);
      //call to non-contract
      error NonContractCall();
      
      function isContract(address account) internal view returns (bool) {
          // According to EIP-1052, 0x0 is the value returned for not-yet created accounts
          // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
          // for accounts without code, i.e. `keccak256('')`
          bytes32 codehash;
          bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
          // solhint-disable-next-line no-inline-assembly
          assembly {
              codehash := extcodehash(account)
          }
          return (codehash != accountHash && codehash != 0x0);
      }
  
      function sendValue(address payable recipient, uint256 amount) internal {
          uint256 balance = address(this).balance;
          if (balance < amount){
              revert InsufficientBalance(balance, amount);
          }
  
          // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
          (bool success, ) = recipient.call{value: amount}("");
          if (!(success)){
              revert SendingValueFail();
          }
      }
  
      function functionCall(address target, bytes memory data) internal returns (bytes memory) {
          return functionCall(target, data, "Address: low-level call failed");
      }
  
      function functionCall(
          address target,
          bytes memory data,
          string memory errorMessage
      ) internal returns (bytes memory) {
          return _functionCallWithValue(target, data, 0, errorMessage);
      }
  
      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");
      }
  
      function functionCallWithValue(
          address target,
          bytes memory data,
          uint256 value,
          string memory errorMessage
      ) internal returns (bytes memory) {
          uint256 balance = address(this).balance;
          if (balance < value){
              revert InsufficientBalanceForCall(balance, value);
          }
          return _functionCallWithValue(target, data, value, errorMessage);
      }
  
      function _functionCallWithValue(
          address target,
          bytes memory data,
          uint256 weiValue,
          string memory errorMessage
      ) private returns (bytes memory) {
          if (!(isContract(target))){
              revert NonContractCall();
          }
  
          // solhint-disable-next-line avoid-low-level-calls
          (bool success, bytes memory returndata) = target.call{value: weiValue}(data);
          if (success) {
              return returndata;
          } else {
              // 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
  
                  // solhint-disable-next-line no-inline-assembly
                  assembly {
                      let returndata_size := mload(returndata)
                      revert(add(32, returndata), returndata_size)
                  }
              } else {
                  revert(errorMessage);
              }
          }
      }
  }
  
  
  
  
  
  
  
  
  
  
  
  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 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 safeApprove(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 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(token).code.length > 0;
      }
  }
  
  
  
  
  
  
  
  
  
  
  contract AdminAuth is AuthHelper {
      using SafeERC20 for IERC20;
  
      AdminVault public constant adminVault = AdminVault(ADMIN_VAULT_ADDR);
  
      error SenderNotOwner();
      error SenderNotAdmin();
  
      modifier onlyOwner() {
          if (adminVault.owner() != msg.sender){
              revert SenderNotOwner();
          }
          _;
      }
  
      modifier onlyAdmin() {
          if (adminVault.admin() != msg.sender){
              revert SenderNotAdmin();
          }
          _;
      }
  
      /// @notice withdraw stuck funds
      function withdrawStuckFunds(address _token, address _receiver, uint256 _amount) public onlyOwner {
          if (_token == 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) {
              payable(_receiver).transfer(_amount);
          } else {
              IERC20(_token).safeTransfer(_receiver, _amount);
          }
      }
  
      /// @notice Destroy the contract
      /// @dev Deprecated method, selfdestruct will soon just send eth
      function kill() public onlyAdmin {
          selfdestruct(payable(msg.sender));
      }
  }
  
  
  
  
  
  
  
  
  contract DFSRegistry is AdminAuth {
      error EntryAlreadyExistsError(bytes4);
      error EntryNonExistentError(bytes4);
      error EntryNotInChangeError(bytes4);
      error ChangeNotReadyError(uint256,uint256);
      error EmptyPrevAddrError(bytes4);
      error AlreadyInContractChangeError(bytes4);
      error AlreadyInWaitPeriodChangeError(bytes4);
  
      event AddNewContract(address,bytes4,address,uint256);
      event RevertToPreviousAddress(address,bytes4,address,address);
      event StartContractChange(address,bytes4,address,address);
      event ApproveContractChange(address,bytes4,address,address);
      event CancelContractChange(address,bytes4,address,address);
      event StartWaitPeriodChange(address,bytes4,uint256);
      event ApproveWaitPeriodChange(address,bytes4,uint256,uint256);
      event CancelWaitPeriodChange(address,bytes4,uint256,uint256);
  
      struct Entry {
          address contractAddr;
          uint256 waitPeriod;
          uint256 changeStartTime;
          bool inContractChange;
          bool inWaitPeriodChange;
          bool exists;
      }
  
      mapping(bytes4 => Entry) public entries;
      mapping(bytes4 => address) public previousAddresses;
  
      mapping(bytes4 => address) public pendingAddresses;
      mapping(bytes4 => uint256) public pendingWaitTimes;
  
      /// @notice Given an contract id returns the registered address
      /// @dev Id is keccak256 of the contract name
      /// @param _id Id of contract
      function getAddr(bytes4 _id) public view returns (address) {
          return entries[_id].contractAddr;
      }
  
      /// @notice Helper function to easily query if id is registered
      /// @param _id Id of contract
      function isRegistered(bytes4 _id) public view returns (bool) {
          return entries[_id].exists;
      }
  
      /////////////////////////// OWNER ONLY FUNCTIONS ///////////////////////////
  
      /// @notice Adds a new contract to the registry
      /// @param _id Id of contract
      /// @param _contractAddr Address of the contract
      /// @param _waitPeriod Amount of time to wait before a contract address can be changed
      function addNewContract(
          bytes4 _id,
          address _contractAddr,
          uint256 _waitPeriod
      ) public onlyOwner {
          if (entries[_id].exists){
              revert EntryAlreadyExistsError(_id);
          }
  
          entries[_id] = Entry({
              contractAddr: _contractAddr,
              waitPeriod: _waitPeriod,
              changeStartTime: 0,
              inContractChange: false,
              inWaitPeriodChange: false,
              exists: true
          });
  
          emit AddNewContract(msg.sender, _id, _contractAddr, _waitPeriod);
      }
  
      /// @notice Reverts to the previous address immediately
      /// @dev In case the new version has a fault, a quick way to fallback to the old contract
      /// @param _id Id of contract
      function revertToPreviousAddress(bytes4 _id) public onlyOwner {
          if (!(entries[_id].exists)){
              revert EntryNonExistentError(_id);
          }
          if (previousAddresses[_id] == address(0)){
              revert EmptyPrevAddrError(_id);
          }
  
          address currentAddr = entries[_id].contractAddr;
          entries[_id].contractAddr = previousAddresses[_id];
  
          emit RevertToPreviousAddress(msg.sender, _id, currentAddr, previousAddresses[_id]);
      }
  
      /// @notice Starts an address change for an existing entry
      /// @dev Can override a change that is currently in progress
      /// @param _id Id of contract
      /// @param _newContractAddr Address of the new contract
      function startContractChange(bytes4 _id, address _newContractAddr) public onlyOwner {
          if (!entries[_id].exists){
              revert EntryNonExistentError(_id);
          }
          if (entries[_id].inWaitPeriodChange){
              revert AlreadyInWaitPeriodChangeError(_id);
          }
  
          entries[_id].changeStartTime = block.timestamp; // solhint-disable-line
          entries[_id].inContractChange = true;
  
          pendingAddresses[_id] = _newContractAddr;
  
          emit StartContractChange(msg.sender, _id, entries[_id].contractAddr, _newContractAddr);
      }
  
      /// @notice Changes new contract address, correct time must have passed
      /// @param _id Id of contract
      function approveContractChange(bytes4 _id) public onlyOwner {
          if (!entries[_id].exists){
              revert EntryNonExistentError(_id);
          }
          if (!entries[_id].inContractChange){
              revert EntryNotInChangeError(_id);
          }
          if (block.timestamp < (entries[_id].changeStartTime + entries[_id].waitPeriod)){// solhint-disable-line
              revert ChangeNotReadyError(block.timestamp, (entries[_id].changeStartTime + entries[_id].waitPeriod));
          }
  
          address oldContractAddr = entries[_id].contractAddr;
          entries[_id].contractAddr = pendingAddresses[_id];
          entries[_id].inContractChange = false;
          entries[_id].changeStartTime = 0;
  
          pendingAddresses[_id] = address(0);
          previousAddresses[_id] = oldContractAddr;
  
          emit ApproveContractChange(msg.sender, _id, oldContractAddr, entries[_id].contractAddr);
      }
  
      /// @notice Cancel pending change
      /// @param _id Id of contract
      function cancelContractChange(bytes4 _id) public onlyOwner {
          if (!entries[_id].exists){
              revert EntryNonExistentError(_id);
          }
          if (!entries[_id].inContractChange){
              revert EntryNotInChangeError(_id);
          }
  
          address oldContractAddr = pendingAddresses[_id];
  
          pendingAddresses[_id] = address(0);
          entries[_id].inContractChange = false;
          entries[_id].changeStartTime = 0;
  
          emit CancelContractChange(msg.sender, _id, oldContractAddr, entries[_id].contractAddr);
      }
  
      /// @notice Starts the change for waitPeriod
      /// @param _id Id of contract
      /// @param _newWaitPeriod New wait time
      function startWaitPeriodChange(bytes4 _id, uint256 _newWaitPeriod) public onlyOwner {
          if (!entries[_id].exists){
              revert EntryNonExistentError(_id);
          }
          if (entries[_id].inContractChange){
              revert AlreadyInContractChangeError(_id);
          }
  
          pendingWaitTimes[_id] = _newWaitPeriod;
  
          entries[_id].changeStartTime = block.timestamp; // solhint-disable-line
          entries[_id].inWaitPeriodChange = true;
  
          emit StartWaitPeriodChange(msg.sender, _id, _newWaitPeriod);
      }
  
      /// @notice Changes new wait period, correct time must have passed
      /// @param _id Id of contract
      function approveWaitPeriodChange(bytes4 _id) public onlyOwner {
          if (!entries[_id].exists){
              revert EntryNonExistentError(_id);
          }
          if (!entries[_id].inWaitPeriodChange){
              revert EntryNotInChangeError(_id);
          }
          if (block.timestamp < (entries[_id].changeStartTime + entries[_id].waitPeriod)){ // solhint-disable-line
              revert ChangeNotReadyError(block.timestamp, (entries[_id].changeStartTime + entries[_id].waitPeriod));
          }
  
          uint256 oldWaitTime = entries[_id].waitPeriod;
          entries[_id].waitPeriod = pendingWaitTimes[_id];
          
          entries[_id].inWaitPeriodChange = false;
          entries[_id].changeStartTime = 0;
  
          pendingWaitTimes[_id] = 0;
  
          emit ApproveWaitPeriodChange(msg.sender, _id, oldWaitTime, entries[_id].waitPeriod);
      }
  
      /// @notice Cancel wait period change
      /// @param _id Id of contract
      function cancelWaitPeriodChange(bytes4 _id) public onlyOwner {
          if (!entries[_id].exists){
              revert EntryNonExistentError(_id);
          }
          if (!entries[_id].inWaitPeriodChange){
              revert EntryNotInChangeError(_id);
          }
  
          uint256 oldWaitPeriod = pendingWaitTimes[_id];
  
          pendingWaitTimes[_id] = 0;
          entries[_id].inWaitPeriodChange = false;
          entries[_id].changeStartTime = 0;
  
          emit CancelWaitPeriodChange(msg.sender, _id, oldWaitPeriod, entries[_id].waitPeriod);
      }
  }
  
  
  
  
  
  
  
  abstract contract DSAuthority {
      function canCall(
          address src,
          address dst,
          bytes4 sig
      ) public view virtual returns (bool);
  }
  
  
  
  
  
  
  
  contract DSAuthEvents {
      event LogSetAuthority(address indexed authority);
      event LogSetOwner(address indexed owner);
  }
  
  contract DSAuth is DSAuthEvents {
      DSAuthority public authority;
      address public owner;
  
      constructor() {
          owner = msg.sender;
          emit LogSetOwner(msg.sender);
      }
  
      function setOwner(address owner_) public auth {
          owner = owner_;
          emit LogSetOwner(owner);
      }
  
      function setAuthority(DSAuthority authority_) public auth {
          authority = authority_;
          emit LogSetAuthority(address(authority));
      }
  
      modifier auth {
          require(isAuthorized(msg.sender, msg.sig), "Not authorized");
          _;
      }
  
      function isAuthorized(address src, bytes4 sig) internal view returns (bool) {
          if (src == address(this)) {
              return true;
          } else if (src == owner) {
              return true;
          } else if (authority == DSAuthority(address(0))) {
              return false;
          } else {
              return authority.canCall(src, address(this), sig);
          }
      }
  }
  
  
  
  
  
  
  
  contract DSNote {
      event LogNote(
          bytes4 indexed sig,
          address indexed guy,
          bytes32 indexed foo,
          bytes32 indexed bar,
          uint256 wad,
          bytes fax
      ) anonymous;
  
      modifier note {
          bytes32 foo;
          bytes32 bar;
  
          assembly {
              foo := calldataload(4)
              bar := calldataload(36)
          }
  
          emit LogNote(msg.sig, msg.sender, foo, bar, msg.value, msg.data);
  
          _;
      }
  }
  
  
  
  
  
  
  
  
  abstract contract DSProxy is DSAuth, DSNote {
      DSProxyCache public cache; // global cache for contracts
  
      constructor(address _cacheAddr) {
          if (!(setCache(_cacheAddr))){
              require(isAuthorized(msg.sender, msg.sig), "Not authorized");
          }
      }
  
      // solhint-disable-next-line no-empty-blocks
      receive() external payable {}
  
      // use the proxy to execute calldata _data on contract _code
      function execute(bytes memory _code, bytes memory _data)
          public
          payable
          virtual
          returns (address target, bytes32 response);
  
      function execute(address _target, bytes memory _data)
          public
          payable
          virtual
          returns (bytes32 response);
  
      //set new cache
      function setCache(address _cacheAddr) public payable virtual returns (bool);
  }
  
  contract DSProxyCache {
      mapping(bytes32 => address) cache;
  
      function read(bytes memory _code) public view returns (address) {
          bytes32 hash = keccak256(_code);
          return cache[hash];
      }
  
      function write(bytes memory _code) public returns (address target) {
          assembly {
              target := create(0, add(_code, 0x20), mload(_code))
              switch iszero(extcodesize(target))
                  case 1 {
                      // throw if contract failed to deploy
                      revert(0, 0)
                  }
          }
          bytes32 hash = keccak256(_code);
          cache[hash] = target;
      }
  }
  
  
  
  
  
  
  
  interface ISafe {
      enum Operation {
          Call,
          DelegateCall
      }
  
      function setup(
          address[] calldata _owners,
          uint256 _threshold,
          address to,
          bytes calldata data,
          address fallbackHandler,
          address paymentToken,
          uint256 payment,
          address payable paymentReceiver
      ) external;
  
      function execTransaction(
          address to,
          uint256 value,
          bytes calldata data,
          Operation operation,
          uint256 safeTxGas,
          uint256 baseGas,
          uint256 gasPrice,
          address gasToken,
          address payable refundReceiver,
          bytes memory signatures
      ) external payable returns (bool success);
  
      function execTransactionFromModule(
          address to,
          uint256 value,
          bytes memory data,
          Operation operation
      ) external returns (bool success);
  
      function checkSignatures(
          bytes32 dataHash,
          bytes memory data,
          bytes memory signatures
      ) external view;
  
      function checkNSignatures(
          address executor,
          bytes32 dataHash,
          bytes memory /* data */,
          bytes memory signatures,
          uint256 requiredSignatures
      ) external view;
  
      function approveHash(bytes32 hashToApprove) external;
  
      function domainSeparator() external view returns (bytes32);
  
      function getTransactionHash(
          address to,
          uint256 value,
          bytes calldata data,
          Operation operation,
          uint256 safeTxGas,
          uint256 baseGas,
          uint256 gasPrice,
          address gasToken,
          address refundReceiver,
          uint256 _nonce
      ) external view returns (bytes32);
  
      function nonce() external view returns (uint256);
  
      function setFallbackHandler(address handler) external;
  
      function getOwners() external view returns (address[] memory);
  
      function isOwner(address owner) external view returns (bool);
  
      function getThreshold() external view returns (uint256);
  
      function enableModule(address module) external;
  
      function isModuleEnabled(address module) external view returns (bool);
  
      function disableModule(address prevModule, address module) external;
  
      function getModulesPaginated(
          address start,
          uint256 pageSize
      ) external view returns (address[] memory array, address next);
  }
  
  
  
  
  
  
  
  interface IDSProxyFactory {
      function isProxy(address _proxy) external view returns (bool);
  }
  
  
  
  
  
  
  
  contract MainnetProxyFactoryAddresses {
      address internal constant PROXY_FACTORY_ADDR = 0xA26e15C895EFc0616177B7c1e7270A4C7D51C997;
  }
  
  
  
  
  
  
  
  contract DSProxyFactoryHelper is MainnetProxyFactoryAddresses {
  }
  
  
  
  
  
  
  
  
  
  contract CheckWalletType is DSProxyFactoryHelper {
      function isDSProxy(address _proxy) public view returns (bool) {
          return IDSProxyFactory(PROXY_FACTORY_ADDR).isProxy(_proxy);
      }
  }
  
  
  
  
  
  
  
  contract DefisaverLogger {
      event RecipeEvent(
          address indexed caller,
          string indexed logName
      );
  
      event ActionDirectEvent(
          address indexed caller,
          string indexed logName,
          bytes data
      );
  
      function logRecipeEvent(
          string memory _logName
      ) public {
          emit RecipeEvent(msg.sender, _logName);
      }
  
      function logActionDirectEvent(
          string memory _logName,
          bytes memory _data
      ) public {
          emit ActionDirectEvent(msg.sender, _logName, _data);
      }
  }
  
  
  
  
  
  
  
  
  
  
  
  
  
  abstract contract ActionBase is AdminAuth, ActionsUtilHelper, CheckWalletType {
      event ActionEvent(
          string indexed logName,
          bytes data
      );
  
      DFSRegistry public constant registry = DFSRegistry(REGISTRY_ADDR);
  
      DefisaverLogger public constant logger = DefisaverLogger(
          DFS_LOGGER_ADDR
      );
  
      //Wrong sub index value
      error SubIndexValueError();
      //Wrong return index value
      error ReturnIndexValueError();
  
      /// @dev Subscription params index range [128, 255]
      uint8 public constant SUB_MIN_INDEX_VALUE = 128;
      uint8 public constant SUB_MAX_INDEX_VALUE = 255;
  
      /// @dev Return params index range [1, 127]
      uint8 public constant RETURN_MIN_INDEX_VALUE = 1;
      uint8 public constant RETURN_MAX_INDEX_VALUE = 127;
  
      /// @dev If the input value should not be replaced
      uint8 public constant NO_PARAM_MAPPING = 0;
  
      /// @dev We need to parse Flash loan actions in a different way
      enum ActionType { FL_ACTION, STANDARD_ACTION, FEE_ACTION, CHECK_ACTION, CUSTOM_ACTION }
  
      /// @notice Parses inputs and runs the implemented action through a user wallet
      /// @dev Is called by the RecipeExecutor chaining actions together
      /// @param _callData Array of input values each value encoded as bytes
      /// @param _subData Array of subscribed vales, replaces input values if specified
      /// @param _paramMapping Array that specifies how return and subscribed values are mapped in input
      /// @param _returnValues Returns values from actions before, which can be injected in inputs
      /// @return Returns a bytes32 value through user wallet, each actions implements what that value is
      function executeAction(
          bytes memory _callData,
          bytes32[] memory _subData,
          uint8[] memory _paramMapping,
          bytes32[] memory _returnValues
      ) public payable virtual returns (bytes32);
  
      /// @notice Parses inputs and runs the single implemented action through a user wallet
      /// @dev Used to save gas when executing a single action directly
      function executeActionDirect(bytes memory _callData) public virtual payable;
  
      /// @notice Returns the type of action we are implementing
      function actionType() public pure virtual returns (uint8);
  
  
      //////////////////////////// HELPER METHODS ////////////////////////////
  
      /// @notice Given an uint256 input, injects return/sub values if specified
      /// @param _param The original input value
      /// @param _mapType Indicated the type of the input in paramMapping
      /// @param _subData Array of subscription data we can replace the input value with
      /// @param _returnValues Array of subscription data we can replace the input value with
      function _parseParamUint(
          uint _param,
          uint8 _mapType,
          bytes32[] memory _subData,
          bytes32[] memory _returnValues
      ) internal pure returns (uint) {
          if (isReplaceable(_mapType)) {
              if (isReturnInjection(_mapType)) {
                  _param = uint(_returnValues[getReturnIndex(_mapType)]);
              } else {
                  _param = uint256(_subData[getSubIndex(_mapType)]);
              }
          }
  
          return _param;
      }
  
  
      /// @notice Given an addr input, injects return/sub values if specified
      /// @param _param The original input value
      /// @param _mapType Indicated the type of the input in paramMapping
      /// @param _subData Array of subscription data we can replace the input value with
      /// @param _returnValues Array of subscription data we can replace the input value with
      function _parseParamAddr(
          address _param,
          uint8 _mapType,
          bytes32[] memory _subData,
          bytes32[] memory _returnValues
      ) internal view returns (address) {
          if (isReplaceable(_mapType)) {
              if (isReturnInjection(_mapType)) {
                  _param = address(bytes20((_returnValues[getReturnIndex(_mapType)])));
              } else {
                  /// @dev The last two values are specially reserved for proxy addr and owner addr
                  if (_mapType == 254) return address(this); // wallet address
                  if (_mapType == 255) return fetchOwnersOrWallet(); // owner if 1/1 wallet or the wallet itself
  
                  _param = address(uint160(uint256(_subData[getSubIndex(_mapType)])));
              }
          }
  
          return _param;
      }
  
      /// @notice Given an bytes32 input, injects return/sub values if specified
      /// @param _param The original input value
      /// @param _mapType Indicated the type of the input in paramMapping
      /// @param _subData Array of subscription data we can replace the input value with
      /// @param _returnValues Array of subscription data we can replace the input value with
      function _parseParamABytes32(
          bytes32 _param,
          uint8 _mapType,
          bytes32[] memory _subData,
          bytes32[] memory _returnValues
      ) internal pure returns (bytes32) {
          if (isReplaceable(_mapType)) {
              if (isReturnInjection(_mapType)) {
                  _param = (_returnValues[getReturnIndex(_mapType)]);
              } else {
                  _param = _subData[getSubIndex(_mapType)];
              }
          }
  
          return _param;
      }
  
      /// @notice Checks if the paramMapping value indicated that we need to inject values
      /// @param _type Indicated the type of the input
      function isReplaceable(uint8 _type) internal pure returns (bool) {
          return _type != NO_PARAM_MAPPING;
      }
  
      /// @notice Checks if the paramMapping value is in the return value range
      /// @param _type Indicated the type of the input
      function isReturnInjection(uint8 _type) internal pure returns (bool) {
          return (_type >= RETURN_MIN_INDEX_VALUE) && (_type <= RETURN_MAX_INDEX_VALUE);
      }
  
      /// @notice Transforms the paramMapping value to the index in return array value
      /// @param _type Indicated the type of the input
      function getReturnIndex(uint8 _type) internal pure returns (uint8) {
          if (!(isReturnInjection(_type))){
              revert SubIndexValueError();
          }
  
          return (_type - RETURN_MIN_INDEX_VALUE);
      }
  
      /// @notice Transforms the paramMapping value to the index in sub array value
      /// @param _type Indicated the type of the input
      function getSubIndex(uint8 _type) internal pure returns (uint8) {
          if (_type < SUB_MIN_INDEX_VALUE){
              revert ReturnIndexValueError();
          }
          return (_type - SUB_MIN_INDEX_VALUE);
      }
  
      function fetchOwnersOrWallet() internal view returns (address) {
          if (isDSProxy(address(this))) 
              return DSProxy(payable(address(this))).owner();
  
          // if not DSProxy, we assume we are in context of Safe
          address[] memory owners = ISafe(address(this)).getOwners();
          return owners.length == 1 ? owners[0] : address(this);
      }
  }
  
  
  
  
  
  
  
  abstract contract IWETH {
      function allowance(address, address) public virtual view returns (uint256);
  
      function balanceOf(address) public virtual view returns (uint256);
  
      function approve(address, uint256) public virtual;
  
      function transfer(address, uint256) public virtual returns (bool);
  
      function transferFrom(
          address,
          address,
          uint256
      ) public virtual returns (bool);
  
      function deposit() public payable virtual;
  
      function withdraw(uint256) public virtual;
  }
  
  
  
  
  
  
  
  
  library TokenUtils {
      using SafeERC20 for IERC20;
  
      address public constant WETH_ADDR = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
      address public constant ETH_ADDR = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
  
      /// @dev Only approves the amount if allowance is lower than amount, does not decrease allowance
      function approveToken(
          address _tokenAddr,
          address _to,
          uint256 _amount
      ) internal {
          if (_tokenAddr == ETH_ADDR) return;
  
          if (IERC20(_tokenAddr).allowance(address(this), _to) < _amount) {
              IERC20(_tokenAddr).safeApprove(_to, _amount);
          }
      }
  
      function pullTokensIfNeeded(
          address _token,
          address _from,
          uint256 _amount
      ) internal returns (uint256) {
          // handle max uint amount
          if (_amount == type(uint256).max) {
              _amount = getBalance(_token, _from);
          }
  
          if (_from != address(0) && _from != address(this) && _token != ETH_ADDR && _amount != 0) {
              IERC20(_token).safeTransferFrom(_from, address(this), _amount);
          }
  
          return _amount;
      }
  
      function withdrawTokens(
          address _token,
          address _to,
          uint256 _amount
      ) internal returns (uint256) {
          if (_amount == type(uint256).max) {
              _amount = getBalance(_token, address(this));
          }
  
          if (_to != address(0) && _to != address(this) && _amount != 0) {
              if (_token != ETH_ADDR) {
                  IERC20(_token).safeTransfer(_to, _amount);
              } else {
                  (bool success, ) = _to.call{value: _amount}("");
                  require(success, "Eth send fail");
              }
          }
  
          return _amount;
      }
  
      function depositWeth(uint256 _amount) internal {
          IWETH(WETH_ADDR).deposit{value: _amount}();
      }
  
      function withdrawWeth(uint256 _amount) internal {
          IWETH(WETH_ADDR).withdraw(_amount);
      }
  
      function getBalance(address _tokenAddr, address _acc) internal view returns (uint256) {
          if (_tokenAddr == ETH_ADDR) {
              return _acc.balance;
          } else {
              return IERC20(_tokenAddr).balanceOf(_acc);
          }
      }
  
      function getTokenDecimals(address _token) internal view returns (uint256) {
          if (_token == ETH_ADDR) return 18;
  
          return IERC20(_token).decimals();
      }
  }
  
  
  
  
  
  
  
  
  
  
  contract AaveV3Supply is ActionBase, AaveV3Helper {
      using TokenUtils for address;
  
      /// @dev enableAsColl - left for backwards compatibility, it's not used in this action
      struct Params {
          uint256 amount;
          address from;
          uint16 assetId;
          bool enableAsColl;
          bool useDefaultMarket;
          bool useOnBehalf;
          address market;
          address onBehalf;
      }
  
      /// @inheritdoc ActionBase
      function executeAction(
          bytes calldata _callData,
          bytes32[] memory _subData,
          uint8[] memory _paramMapping,
          bytes32[] memory _returnValues
      ) public payable virtual override returns (bytes32) {
          Params memory params = parseInputs(_callData);
  
          params.amount = _parseParamUint(params.amount, _paramMapping[0], _subData, _returnValues);
          params.from = _parseParamAddr(params.from, _paramMapping[1], _subData, _returnValues);
          params.assetId = uint16(_parseParamUint(params.assetId, _paramMapping[2], _subData, _returnValues));
          params.useDefaultMarket = _parseParamUint(params.useDefaultMarket ? 1 : 0, _paramMapping[4], _subData, _returnValues) == 1;
          params.useOnBehalf = _parseParamUint(params.useOnBehalf ? 1 : 0, _paramMapping[5], _subData, _returnValues) == 1;
          params.market = _parseParamAddr(params.market, _paramMapping[6], _subData, _returnValues);
          params.onBehalf = _parseParamAddr(
              params.onBehalf,
              _paramMapping[7],
              _subData,
              _returnValues
          );
  
          (uint256 supplyAmount, bytes memory logData) = _supply(
              params.market,
              params.amount,
              params.from,
              params.assetId,
              params.onBehalf
          );
          emit ActionEvent("AaveV3Supply", logData);
          return bytes32(supplyAmount);
      }
  
      /// @inheritdoc ActionBase
      function executeActionDirect(bytes calldata _callData) public payable override {
          Params memory params = parseInputs(_callData);
          (, bytes memory logData) = _supply(
              params.market,
              params.amount,
              params.from,
              params.assetId,
              params.onBehalf
          );
          logger.logActionDirectEvent("AaveV3Supply", logData);
      }
  
      function executeActionDirectL2() public payable {
          Params memory params = decodeInputs(msg.data[4:]);
          (, bytes memory logData) = _supply(
              params.market,
              params.amount,
              params.from,
              params.assetId,
              params.onBehalf
          );
          logger.logActionDirectEvent("AaveV3Supply", logData);
      }
  
      /// @inheritdoc ActionBase
      function actionType() public pure virtual override returns (uint8) {
          return uint8(ActionType.STANDARD_ACTION);
      }
  
      //////////////////////////// ACTION LOGIC ////////////////////////////
  
      /// @notice User deposits tokens to the Aave protocol
      /// @dev User needs to approve its wallet to pull the tokens being supplied
      /// @param _market Address provider for specific market
      /// @param _amount Amount of tokens to be deposited
      /// @param _from Where are we pulling the supply tokens amount from
      /// @param _assetId The id of the token to be deposited
      /// @param _onBehalf For what user we are supplying the tokens, defaults to user's wallet
      function _supply(
          address _market,
          uint256 _amount,
          address _from,
          uint16 _assetId,
          address _onBehalf
      ) internal returns (uint256, bytes memory) {
          IPoolV3 lendingPool = getLendingPool(_market);
          address tokenAddr = lendingPool.getReserveAddressById(_assetId);
  
          // if amount is set to max, take the whole _from balance
          if (_amount == type(uint256).max) {
              _amount = tokenAddr.getBalance(_from);
          }
  
          // default to onBehalf of user's wallet
          if (_onBehalf == address(0)) {
              _onBehalf = address(this);
          }
  
          // pull tokens to user's wallet so we can supply
          tokenAddr.pullTokensIfNeeded(_from, _amount);
  
          // approve aave pool to pull tokens
          tokenAddr.approveToken(address(lendingPool), _amount);
  
          lendingPool.supply(tokenAddr, _amount, _onBehalf, AAVE_REFERRAL_CODE);
  
          bytes memory logData = abi.encode(
              _market,
              tokenAddr,
              _amount,
              _from,
              _onBehalf
          );
          return (_amount, logData);
      }
  
      function parseInputs(bytes memory _callData) public pure returns (Params memory params) {
          params = abi.decode(_callData, (Params));
          if (params.useDefaultMarket) {
              params.market = DEFAULT_AAVE_MARKET;
          }
          if (!params.useOnBehalf) {
              params.onBehalf = address(0);
          }
      }
  
      function encodeInputs(Params memory _params) public pure returns (bytes memory encodedInput) {
          encodedInput = bytes.concat(this.executeActionDirectL2.selector);
          encodedInput = bytes.concat(encodedInput, bytes32(_params.amount));
          encodedInput = bytes.concat(encodedInput, bytes20(_params.from));
          encodedInput = bytes.concat(encodedInput, bytes2(_params.assetId));
          encodedInput = bytes.concat(encodedInput, boolToBytes(_params.enableAsColl));
          encodedInput = bytes.concat(encodedInput, boolToBytes(_params.useDefaultMarket));
          encodedInput = bytes.concat(encodedInput, boolToBytes(_params.useOnBehalf));
          if (!_params.useDefaultMarket) {
              encodedInput = bytes.concat(encodedInput, bytes20(_params.market));
          }
          if (_params.useOnBehalf) {
              encodedInput = bytes.concat(encodedInput, bytes20(_params.onBehalf));
          }
      }
  
      function decodeInputs(bytes calldata _encodedInput) public pure returns (Params memory params) {
          params.amount = uint256(bytes32(_encodedInput[0:32]));
          params.from = address(bytes20(_encodedInput[32:52]));
          params.assetId = uint16(bytes2(_encodedInput[52:54]));
          params.enableAsColl = bytesToBool(bytes1(_encodedInput[54:55]));
          params.useDefaultMarket = bytesToBool(bytes1(_encodedInput[55:56]));
          params.useOnBehalf = bytesToBool(bytes1(_encodedInput[56:57]));
          uint256 mark = 57;
  
          if (params.useDefaultMarket) {
              params.market = DEFAULT_AAVE_MARKET;
          } else {
              params.market = address(bytes20(_encodedInput[mark:mark + 20]));
              mark += 20;
          }
  
          if (params.useOnBehalf) {
              params.onBehalf = address(bytes20(_encodedInput[mark:mark + 20]));
          } else {
              params.onBehalf = address(0);
          }
      }
  }
  