BERA Price: $3.72 (+1.02%)

Contract

0x4aA31Fb17bD73a5D43Be5357B650790daA742B14

Overview

BERA Balance

Berachain LogoBerachain LogoBerachain Logo0 BERA

BERA Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Zap In40881212025-04-23 13:32:535 mins ago1745415173IN
0x4aA31Fb1...daA742B14
0 BERA00.00000007
Zap Out40878652025-04-23 13:24:2014 mins ago1745414660IN
0x4aA31Fb1...daA742B14
0 BERA0.000139840.15
Zap In40861442025-04-23 12:27:431 hr ago1745411263IN
0x4aA31Fb1...daA742B14
0 BERA0.000001850.001321
Zap Out40851022025-04-23 11:53:291 hr ago1745409209IN
0x4aA31Fb1...daA742B14
0 BERA0.000334070.5
Zap Out40842922025-04-23 11:26:492 hrs ago1745407609IN
0x4aA31Fb1...daA742B14
0 BERA00.00000006
Zap Out40842232025-04-23 11:24:322 hrs ago1745407472IN
0x4aA31Fb1...daA742B14
0 BERA0.000319580.5
Zap In40826292025-04-23 10:32:153 hrs ago1745404335IN
0x4aA31Fb1...daA742B14
0 BERA0.000026520.15832056
Zap Out40803712025-04-23 9:18:154 hrs ago1745399895IN
0x4aA31Fb1...daA742B14
0 BERA0.000320770.5
Zap Out40773912025-04-23 7:40:095 hrs ago1745394009IN
0x4aA31Fb1...daA742B14
0 BERA00.00000007
Zap Out40773752025-04-23 7:39:365 hrs ago1745393976IN
0x4aA31Fb1...daA742B14
0 BERA00.00000007
Zap Out40773572025-04-23 7:39:025 hrs ago1745393942IN
0x4aA31Fb1...daA742B14
0 BERA00.00000007
Zap Out40773442025-04-23 7:38:375 hrs ago1745393917IN
0x4aA31Fb1...daA742B14
0 BERA00.00000007
Zap Out40764992025-04-23 7:10:436 hrs ago1745392243IN
0x4aA31Fb1...daA742B14
0 BERA0.000329970.5000006
Zap In40761172025-04-23 6:58:036 hrs ago1745391483IN
0x4aA31Fb1...daA742B14
0 BERA00.00000007
Zap In40757772025-04-23 6:46:486 hrs ago1745390808IN
0x4aA31Fb1...daA742B14
0 BERA00.00000193
Zap Out40752362025-04-23 6:29:017 hrs ago1745389741IN
0x4aA31Fb1...daA742B14
0 BERA0.000002760.003073
Zap In40720872025-04-23 4:45:288 hrs ago1745383528IN
0x4aA31Fb1...daA742B14
0.00002619 BERA00.00000007
Zap Out40718922025-04-23 4:39:028 hrs ago1745383142IN
0x4aA31Fb1...daA742B14
0 BERA00.00000007
Zap In40718252025-04-23 4:36:489 hrs ago1745383008IN
0x4aA31Fb1...daA742B14
0.15499999 BERA00.00000006
Zap In40717922025-04-23 4:35:459 hrs ago1745382945IN
0x4aA31Fb1...daA742B14
0.00030392 BERA00.00000006
Zap In40717912025-04-23 4:35:429 hrs ago1745382942IN
0x4aA31Fb1...daA742B14
0.15499999 BERA00.00000006
Zap In40714432025-04-23 4:24:189 hrs ago1745382258IN
0x4aA31Fb1...daA742B14
0 BERA0.000012210.079376
Zap In40710752025-04-23 4:12:099 hrs ago1745381529IN
0x4aA31Fb1...daA742B14
7.47979026 BERA00.00000009
Zap Out40696292025-04-23 3:24:3110 hrs ago1745378671IN
0x4aA31Fb1...daA742B14
0 BERA0.000000110.00017108
Zap Out40686392025-04-23 2:51:4310 hrs ago1745376703IN
0x4aA31Fb1...daA742B14
0 BERA00.00000159
View all transactions

Latest 25 internal transactions (View All)

Parent Transaction Hash Block From To
40851022025-04-23 11:53:291 hr ago1745409209
0x4aA31Fb1...daA742B14
17.15338595 BERA
40851022025-04-23 11:53:291 hr ago1745409209
0x4aA31Fb1...daA742B14
17.15338595 BERA
40842922025-04-23 11:26:492 hrs ago1745407609
0x4aA31Fb1...daA742B14
70.11935048 BERA
40842922025-04-23 11:26:492 hrs ago1745407609
0x4aA31Fb1...daA742B14
70.11935048 BERA
40842232025-04-23 11:24:322 hrs ago1745407472
0x4aA31Fb1...daA742B14
94.83124387 BERA
40842232025-04-23 11:24:322 hrs ago1745407472
0x4aA31Fb1...daA742B14
94.83124387 BERA
40803712025-04-23 9:18:154 hrs ago1745399895
0x4aA31Fb1...daA742B14
40.04120825 BERA
40803712025-04-23 9:18:154 hrs ago1745399895
0x4aA31Fb1...daA742B14
40.04120825 BERA
40773912025-04-23 7:40:095 hrs ago1745394009
0x4aA31Fb1...daA742B14
0.04078295 BERA
40773912025-04-23 7:40:095 hrs ago1745394009
0x4aA31Fb1...daA742B14
0.04078295 BERA
40773752025-04-23 7:39:365 hrs ago1745393976
0x4aA31Fb1...daA742B14
0.01023044 BERA
40773752025-04-23 7:39:365 hrs ago1745393976
0x4aA31Fb1...daA742B14
0.01023044 BERA
40773572025-04-23 7:39:025 hrs ago1745393942
0x4aA31Fb1...daA742B14
0.02936785 BERA
40773572025-04-23 7:39:025 hrs ago1745393942
0x4aA31Fb1...daA742B14
0.02936785 BERA
40773442025-04-23 7:38:375 hrs ago1745393917
0x4aA31Fb1...daA742B14
0.0602096 BERA
40773442025-04-23 7:38:375 hrs ago1745393917
0x4aA31Fb1...daA742B14
0.0602096 BERA
40764992025-04-23 7:10:436 hrs ago1745392243
0x4aA31Fb1...daA742B14
112.38976737 BERA
40764992025-04-23 7:10:436 hrs ago1745392243
0x4aA31Fb1...daA742B14
112.38976737 BERA
40720872025-04-23 4:45:288 hrs ago1745383528
0x4aA31Fb1...daA742B14
0.00000029 BERA
40720872025-04-23 4:45:288 hrs ago1745383528
0x4aA31Fb1...daA742B14
0.00000029 BERA
40720872025-04-23 4:45:288 hrs ago1745383528
0x4aA31Fb1...daA742B14
0.00002619 BERA
40718922025-04-23 4:39:028 hrs ago1745383142
0x4aA31Fb1...daA742B14
0.00007446 BERA
40718922025-04-23 4:39:028 hrs ago1745383142
0x4aA31Fb1...daA742B14
0.00007446 BERA
40718252025-04-23 4:36:489 hrs ago1745383008
0x4aA31Fb1...daA742B14
0.00174606 BERA
40718252025-04-23 4:36:489 hrs ago1745383008
0x4aA31Fb1...daA742B14
0.00174606 BERA
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
KodiakZapper

Compiler Version
v0.8.20+commit.a1b79de6

Optimization Enabled:
Yes with 100 runs

Other Settings:
paris EvmVersion
File 1 of 18 : KodiakZapper.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

import {ZapperBase} from "./ZapperBase.sol";
import {IKodiakVaultV1, IKodiakV1RouterStaking} from "../interfaces/kodiak/IKodiak.sol";
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {IVault} from "../interfaces/IVault.sol";
import {ISwapRouter} from "../interfaces/ISwapRouter.sol";
import {IDexType} from "../interfaces/IDexType.sol";

/**
 * @title KodiakZapper
 * @notice A zapper contract that enables single-token deposits and withdrawals for Kodiak vaults
 * @dev This contract handles the conversion between a single input token and the token pair required by Kodiak vaults.
 * It manages the optimal ratio calculation and token swaps to match Kodiak's deposit requirements.
 *
 * Key features:
 * - Single token deposits: Users can deposit a single token which gets converted to the required token pair
 * - Optimal ratio calculation: Automatically determines the best token ratio for deposits
 * - Withdrawal to single token: Converts withdrawn token pairs back to a single desired token
 * - Supports both native token and ERC20 deposits
 */
contract KodiakZapper is ZapperBase {
  using SafeERC20 for IERC20;

  constructor(
    address governanceAddress,
    address wrappedNative,
    address stableCoin,
    address swapRouter,
    address lpRouter,
    address feeRecipient,
    uint16 zapInFee,
    uint16 zapOutFee
  ) ZapperBase(governanceAddress, wrappedNative, stableCoin, swapRouter, lpRouter, feeRecipient, zapInFee, zapOutFee) {}

  /**
   * @notice Converts input token amount to vault's asset
   * @param asset The vault's asset
   * @param tokenIn The token being deposited
   * @param tokenInAmount The amount of tokens being deposited
   * @param recipient The address to receive the asset
   * @return tokenOutAmount Amount of asset received
   * @return returnedAssets Array of remaining token balances to be returned to the user
   */
  function swapToAssets(
    address asset,
    address tokenIn,
    uint256 tokenInAmount,
    address recipient
  ) public override returns (uint256 tokenOutAmount, ReturnedAsset[] memory returnedAssets) {
    if (recipient != address(this)) {
      tokenInAmount = _safeTransferFromTokens(tokenIn, tokenInAmount);
    }

    IERC20(tokenIn).safeTransfer(address(lpRouter), tokenInAmount);
    tokenOutAmount = lpRouter.addLiquidity(asset, tokenIn, tokenInAmount, recipient, IDexType.DexType.KODIAK_V3);

    returnedAssets = _returnAsset(tokenIn, msg.sender);
  }

  /**
   * @notice Converts vault's asset to output token
   * @param asset The vault's asset
   * @param tokenOut The token the user wants to receive
   * @param assetsInAmount The amount of LP tokens being withdrawn
   * @param recipient The address to receive the output token
   * @return tokenOutAmount The amount of desired tokens received
   * @return returnedAssets Array of remaining token balances to be returned to the user
   */
  function swapFromAssets(
    address asset,
    address tokenOut,
    uint256 assetsInAmount,
    address recipient
  ) public override returns (uint256 tokenOutAmount, ReturnedAsset[] memory returnedAssets) {
    if (recipient != address(this)) {
      assetsInAmount = _safeTransferFromTokens(asset, assetsInAmount);
    }

    //send assetsIn to lpRouter
    IERC20(asset).safeTransfer(address(lpRouter), assetsInAmount);

    tokenOutAmount = lpRouter.removeLiquidity(
      asset,
      assetsInAmount,
      address(this),
      tokenOut,
      IDexType.DexType.KODIAK_V3
    );

    if (recipient != address(this)) {
      returnedAssets = _returnAsset(tokenOut, recipient);
    }
  }
}

File 2 of 18 : IERC4626.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC4626.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../token/ERC20/IERC20.sol";
import {IERC20Metadata} from "../token/ERC20/extensions/IERC20Metadata.sol";

/**
 * @dev Interface of the ERC4626 "Tokenized Vault Standard", as defined in
 * https://eips.ethereum.org/EIPS/eip-4626[ERC-4626].
 */
interface IERC4626 is IERC20, IERC20Metadata {
    event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares);

    event Withdraw(
        address indexed sender,
        address indexed receiver,
        address indexed owner,
        uint256 assets,
        uint256 shares
    );

    /**
     * @dev Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing.
     *
     * - MUST be an ERC-20 token contract.
     * - MUST NOT revert.
     */
    function asset() external view returns (address assetTokenAddress);

    /**
     * @dev Returns the total amount of the underlying asset that is “managed” by Vault.
     *
     * - SHOULD include any compounding that occurs from yield.
     * - MUST be inclusive of any fees that are charged against assets in the Vault.
     * - MUST NOT revert.
     */
    function totalAssets() external view returns (uint256 totalManagedAssets);

    /**
     * @dev Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an ideal
     * scenario where all the conditions are met.
     *
     * - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
     * - MUST NOT show any variations depending on the caller.
     * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
     * - MUST NOT revert.
     *
     * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
     * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
     * from.
     */
    function convertToShares(uint256 assets) external view returns (uint256 shares);

    /**
     * @dev Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an ideal
     * scenario where all the conditions are met.
     *
     * - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
     * - MUST NOT show any variations depending on the caller.
     * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
     * - MUST NOT revert.
     *
     * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
     * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
     * from.
     */
    function convertToAssets(uint256 shares) external view returns (uint256 assets);

    /**
     * @dev Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver,
     * through a deposit call.
     *
     * - MUST return a limited value if receiver is subject to some deposit limit.
     * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited.
     * - MUST NOT revert.
     */
    function maxDeposit(address receiver) external view returns (uint256 maxAssets);

    /**
     * @dev Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given
     * current on-chain conditions.
     *
     * - MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit
     *   call in the same transaction. I.e. deposit should return the same or more shares as previewDeposit if called
     *   in the same transaction.
     * - MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the
     *   deposit would be accepted, regardless if the user has enough tokens approved, etc.
     * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
     * - MUST NOT revert.
     *
     * NOTE: any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in
     * share price or some other type of condition, meaning the depositor will lose assets by depositing.
     */
    function previewDeposit(uint256 assets) external view returns (uint256 shares);

    /**
     * @dev Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens.
     *
     * - MUST emit the Deposit event.
     * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
     *   deposit execution, and are accounted for during deposit.
     * - MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not
     *   approving enough underlying tokens to the Vault contract, etc).
     *
     * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
     */
    function deposit(uint256 assets, address receiver) external returns (uint256 shares);

    /**
     * @dev Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call.
     * - MUST return a limited value if receiver is subject to some mint limit.
     * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted.
     * - MUST NOT revert.
     */
    function maxMint(address receiver) external view returns (uint256 maxShares);

    /**
     * @dev Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given
     * current on-chain conditions.
     *
     * - MUST return as close to and no fewer than the exact amount of assets that would be deposited in a mint call
     *   in the same transaction. I.e. mint should return the same or fewer assets as previewMint if called in the
     *   same transaction.
     * - MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint
     *   would be accepted, regardless if the user has enough tokens approved, etc.
     * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
     * - MUST NOT revert.
     *
     * NOTE: any unfavorable discrepancy between convertToAssets and previewMint SHOULD be considered slippage in
     * share price or some other type of condition, meaning the depositor will lose assets by minting.
     */
    function previewMint(uint256 shares) external view returns (uint256 assets);

    /**
     * @dev Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens.
     *
     * - MUST emit the Deposit event.
     * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint
     *   execution, and are accounted for during mint.
     * - MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not
     *   approving enough underlying tokens to the Vault contract, etc).
     *
     * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
     */
    function mint(uint256 shares, address receiver) external returns (uint256 assets);

    /**
     * @dev Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the
     * Vault, through a withdraw call.
     *
     * - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
     * - MUST NOT revert.
     */
    function maxWithdraw(address owner) external view returns (uint256 maxAssets);

    /**
     * @dev Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block,
     * given current on-chain conditions.
     *
     * - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw
     *   call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if
     *   called
     *   in the same transaction.
     * - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though
     *   the withdrawal would be accepted, regardless if the user has enough shares, etc.
     * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
     * - MUST NOT revert.
     *
     * NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in
     * share price or some other type of condition, meaning the depositor will lose assets by depositing.
     */
    function previewWithdraw(uint256 assets) external view returns (uint256 shares);

    /**
     * @dev Burns shares from owner and sends exactly assets of underlying tokens to receiver.
     *
     * - MUST emit the Withdraw event.
     * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
     *   withdraw execution, and are accounted for during withdraw.
     * - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner
     *   not having enough shares, etc).
     *
     * Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
     * Those methods should be performed separately.
     */
    function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares);

    /**
     * @dev Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault,
     * through a redeem call.
     *
     * - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
     * - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock.
     * - MUST NOT revert.
     */
    function maxRedeem(address owner) external view returns (uint256 maxShares);

    /**
     * @dev Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block,
     * given current on-chain conditions.
     *
     * - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call
     *   in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the
     *   same transaction.
     * - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the
     *   redemption would be accepted, regardless if the user has enough shares, etc.
     * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
     * - MUST NOT revert.
     *
     * NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in
     * share price or some other type of condition, meaning the depositor will lose assets by redeeming.
     */
    function previewRedeem(uint256 shares) external view returns (uint256 assets);

    /**
     * @dev Burns exactly shares from owner and sends assets of underlying tokens to receiver.
     *
     * - MUST emit the Withdraw event.
     * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
     *   redeem execution, and are accounted for during redeem.
     * - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner
     *   not having enough shares, etc).
     *
     * NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
     * Those methods should be performed separately.
     */
    function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets);
}

File 3 of 18 : IERC20Metadata.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../IERC20.sol";

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 */
interface IERC20Metadata is IERC20 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the symbol of the token.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}

File 4 of 18 : IERC20Permit.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 *
 * ==== Security Considerations
 *
 * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
 * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
 * considered as an intention to spend the allowance in any specific way. The second is that because permits have
 * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
 * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
 * generally recommended is:
 *
 * ```solidity
 * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
 *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
 *     doThing(..., value);
 * }
 *
 * function doThing(..., uint256 value) public {
 *     token.safeTransferFrom(msg.sender, address(this), value);
 *     ...
 * }
 * ```
 *
 * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
 * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
 * {SafeERC20-safeTransferFrom}).
 *
 * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
 * contracts should have entry points that don't rely on permit.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     *
     * CAUTION: See Security Considerations above.
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @dev Returns the current nonce for `owner`. This value must be
     * included whenever a signature is generated for {permit}.
     *
     * Every successful call to {permit} increases ``owner``'s nonce by one. This
     * prevents a signature from being used multiple times.
     */
    function nonces(address owner) external view returns (uint256);

    /**
     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}

File 5 of 18 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the value of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the value of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 value) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the
     * allowance mechanism. `value` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 value) external returns (bool);
}

File 6 of 18 : SafeERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../utils/Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    using Address for address;

    /**
     * @dev An operation with an ERC20 token failed.
     */
    error SafeERC20FailedOperation(address token);

    /**
     * @dev Indicates a failed `decreaseAllowance` request.
     */
    error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);

    /**
     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
    }

    /**
     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
     */
    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
    }

    /**
     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        forceApprove(token, spender, oldAllowance + value);
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
     * value, non-reverting calls are assumed to be successful.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
        unchecked {
            uint256 currentAllowance = token.allowance(address(this), spender);
            if (currentAllowance < requestedDecrease) {
                revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
            }
            forceApprove(token, spender, currentAllowance - requestedDecrease);
        }
    }

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
     * to be set to zero before setting it to a non-zero value, such as USDT.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));

        if (!_callOptionalReturnBool(token, approvalCall)) {
            _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
            _callOptionalReturn(token, approvalCall);
        }
    }

    /**
     * @dev 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);
        if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturn} that 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;
    }
}

File 7 of 18 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)

pragma solidity ^0.8.20;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev The ETH balance of the account is not enough to perform the operation.
     */
    error AddressInsufficientBalance(address account);

    /**
     * @dev There's no code at `target` (it is not a contract).
     */
    error AddressEmptyCode(address target);

    /**
     * @dev A call to an address target failed. The target may have reverted.
     */
    error FailedInnerCall();

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        if (address(this).balance < amount) {
            revert AddressInsufficientBalance(address(this));
        }

        (bool success, ) = recipient.call{value: amount}("");
        if (!success) {
            revert FailedInnerCall();
        }
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason or custom error, it is bubbled
     * up by this function (like regular Solidity function calls). However, if
     * the call reverted with no returned reason, this function reverts with a
     * {FailedInnerCall} error.
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        if (address(this).balance < value) {
            revert AddressInsufficientBalance(address(this));
        }
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
     * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
     * unsuccessful call.
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata
    ) internal view returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            // only check if target is a contract if the call was successful and the return data is empty
            // otherwise we already know that it was a contract
            if (returndata.length == 0 && target.code.length == 0) {
                revert AddressEmptyCode(target);
            }
            return returndata;
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
     * revert reason or with a default {FailedInnerCall} error.
     */
    function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            return returndata;
        }
    }

    /**
     * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
     */
    function _revert(bytes memory returndata) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert FailedInnerCall();
        }
    }
}

File 8 of 18 : ReentrancyGuard.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol)

pragma solidity ^0.8.20;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant NOT_ENTERED = 1;
    uint256 private constant ENTERED = 2;

    uint256 private _status;

    /**
     * @dev Unauthorized reentrant call.
     */
    error ReentrancyGuardReentrantCall();

    constructor() {
        _status = NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _status will be NOT_ENTERED
        if (_status == ENTERED) {
            revert ReentrancyGuardReentrantCall();
        }

        // Any calls to nonReentrant after this point will fail
        _status = ENTERED;
    }

    function _nonReentrantAfter() private {
        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = NOT_ENTERED;
    }

    /**
     * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
     * `nonReentrant` function in the call stack.
     */
    function _reentrancyGuardEntered() internal view returns (bool) {
        return _status == ENTERED;
    }
}

File 9 of 18 : Beraswap.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

interface IAsset {
  // solhint-disable-previous-line no-empty-blocks
}

interface IBeraVault {
  struct SingleSwap {
    bytes32 poolId;
    SwapKind kind;
    IAsset assetIn;
    IAsset assetOut;
    uint256 amount;
    bytes userData;
  }

  struct BatchSwapStep {
    bytes32 poolId;
    uint256 assetInIndex;
    uint256 assetOutIndex;
    uint256 amount;
    bytes userData;
  }

  struct FundManagement {
    address sender;
    bool fromInternalBalance;
    address payable recipient;
    bool toInternalBalance;
  }

  struct JoinPoolRequest {
    IAsset[] assets;
    uint256[] maxAmountsIn;
    bytes userData;
    bool fromInternalBalance;
  }

  struct ExitPoolRequest {
    IAsset[] assets;
    uint256[] minAmountsOut;
    bytes userData;
    bool toInternalBalance;
  }

  enum PoolSpecialization {
    GENERAL,
    MINIMAL_SWAP_INFO,
    TWO_TOKEN
  }

  enum SwapKind {
    GIVEN_IN,
    GIVEN_OUT
  }

  function getPoolTokens(
    bytes32 poolId
  ) external view returns (IERC20[] memory tokens, uint256[] memory balances, uint256 lastChangeBlock);

  function getPool(bytes32 poolId) external view returns (address, PoolSpecialization);

  function swap(
    SingleSwap memory singleSwap,
    FundManagement memory funds,
    uint256 limit,
    uint256 deadline
  ) external returns (uint256 amountCalculated);

  function batchSwap(
    SwapKind kind,
    BatchSwapStep[] memory swaps,
    IAsset[] memory assets,
    FundManagement memory funds,
    int256[] memory limits,
    uint256 deadline
  ) external payable returns (int256[] memory);

  function joinPool(bytes32 poolId, address sender, address recipient, JoinPoolRequest memory request) external payable;

  function exitPool(bytes32 poolId, address sender, address payable recipient, ExitPoolRequest memory request) external;
}

interface IBeraPool {
  function getPoolId() external view returns (bytes32);

  function getVault() external view returns (address);

  function balanceOf(address account) external view returns (uint256);

  function version() external view returns (string memory);
}

File 10 of 18 : IWETH.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

interface IWETH is IERC20 {
  function name() external view returns (string memory);

  function approve(address guy, uint256 wad) external returns (bool);

  function totalSupply() external view returns (uint256);

  function transferFrom(address src, address dst, uint256 wad) external returns (bool);

  function withdraw(uint256 wad) external;

  function withdrawTo(address account, uint256 amount) external;

  function decimals() external view returns (uint8);

  function balanceOf(address) external view returns (uint256);

  function symbol() external view returns (string memory);

  function transfer(address dst, uint256 wad) external returns (bool);

  function deposit() external payable;

  function allowance(address, address) external view returns (uint256);
}

File 11 of 18 : UniswapV3.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.20;

interface IUniswapV3Router {
  struct ExactInputSingleParams {
    address tokenIn;
    address tokenOut;
    uint24 fee;
    address recipient;
    uint256 deadline;
    uint256 amountIn;
    uint256 amountOutMinimum;
    uint160 sqrtPriceLimitX96;
  }

  /// @notice Swaps `amountIn` of one token for as much as possible of another token
  /// @param params The parameters necessary for the swap, encoded as `ExactInputSingleParams` in calldata
  /// @return amountOut The amount of the received token
  function exactInputSingle(ExactInputSingleParams calldata params) external payable returns (uint256 amountOut);

  struct ExactInputParams {
    bytes path;
    address recipient;
    uint256 deadline;
    uint256 amountIn;
    uint256 amountOutMinimum;
  }

  /// @notice Swaps `amountIn` of one token for as much as possible of another along the specified path
  /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactInputParams` in calldata
  /// @return amountOut The amount of the received token
  function exactInput(ExactInputParams calldata params) external payable returns (uint256 amountOut);

  struct ExactOutputSingleParams {
    address tokenIn;
    address tokenOut;
    uint24 fee;
    address recipient;
    uint256 deadline;
    uint256 amountOut;
    uint256 amountInMaximum;
    uint160 sqrtPriceLimitX96;
  }

  /// @notice Swaps as little as possible of one token for `amountOut` of another token
  /// @param params The parameters necessary for the swap, encoded as `ExactOutputSingleParams` in calldata
  /// @return amountIn The amount of the input token
  function exactOutputSingle(ExactOutputSingleParams calldata params) external payable returns (uint256 amountIn);

  struct ExactOutputParams {
    bytes path;
    address recipient;
    uint256 deadline;
    uint256 amountOut;
    uint256 amountInMaximum;
  }

  /// @notice Swaps as little as possible of one token for `amountOut` of another along the specified path (reversed)
  /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactOutputParams` in calldata
  /// @return amountIn The amount of the input token
  function exactOutput(ExactOutputParams calldata params) external payable returns (uint256 amountIn);
}

/// @title QuoterV2 Interface
/// @notice Supports quoting the calculated amounts from exact input or exact output swaps.
/// @notice For each pool also tells you the number of initialized ticks crossed and the sqrt price of the pool after the swap.
/// @dev These functions are not marked view because they rely on calling non-view functions and reverting
/// to compute the result. They are also not gas efficient and should not be called on-chain.
interface IQuoterV2 {
  /// @notice Returns the amount out received for a given exact input swap without executing the swap
  /// @param path The path of the swap, i.e. each token pair and the pool fee
  /// @param amountIn The amount of the first token to swap
  /// @return amountOut The amount of the last token that would be received
  /// @return sqrtPriceX96AfterList List of the sqrt price after the swap for each pool in the path
  /// @return initializedTicksCrossedList List of the initialized ticks that the swap crossed for each pool in the path
  /// @return gasEstimate The estimate of the gas that the swap consumes
  function quoteExactInput(
    bytes memory path,
    uint256 amountIn
  )
    external
    returns (
      uint256 amountOut,
      uint160[] memory sqrtPriceX96AfterList,
      uint32[] memory initializedTicksCrossedList,
      uint256 gasEstimate
    );

  struct QuoteExactInputSingleParams {
    address tokenIn;
    address tokenOut;
    uint256 amountIn;
    uint24 fee;
    uint160 sqrtPriceLimitX96;
  }

  /// @notice Returns the amount out received for a given exact input but for a swap of a single pool
  /// @param params The params for the quote, encoded as `QuoteExactInputSingleParams`
  /// tokenIn The token being swapped in
  /// tokenOut The token being swapped out
  /// fee The fee of the token pool to consider for the pair
  /// amountIn The desired input amount
  /// sqrtPriceLimitX96 The price limit of the pool that cannot be exceeded by the swap
  /// @return amountOut The amount of `tokenOut` that would be received
  /// @return sqrtPriceX96After The sqrt price of the pool after the swap
  /// @return initializedTicksCrossed The number of initialized ticks that the swap crossed
  /// @return gasEstimate The estimate of the gas that the swap consumes
  function quoteExactInputSingle(
    QuoteExactInputSingleParams memory params
  )
    external
    returns (uint256 amountOut, uint160 sqrtPriceX96After, uint32 initializedTicksCrossed, uint256 gasEstimate);

  /// @notice Returns the amount in required for a given exact output swap without executing the swap
  /// @param path The path of the swap, i.e. each token pair and the pool fee. Path must be provided in reverse order
  /// @param amountOut The amount of the last token to receive
  /// @return amountIn The amount of first token required to be paid
  /// @return sqrtPriceX96AfterList List of the sqrt price after the swap for each pool in the path
  /// @return initializedTicksCrossedList List of the initialized ticks that the swap crossed for each pool in the path
  /// @return gasEstimate The estimate of the gas that the swap consumes
  function quoteExactOutput(
    bytes memory path,
    uint256 amountOut
  )
    external
    returns (
      uint256 amountIn,
      uint160[] memory sqrtPriceX96AfterList,
      uint32[] memory initializedTicksCrossedList,
      uint256 gasEstimate
    );

  struct QuoteExactOutputSingleParams {
    address tokenIn;
    address tokenOut;
    uint256 amount;
    uint24 fee;
    uint160 sqrtPriceLimitX96;
  }

  /// @notice Returns the amount in required to receive the given exact output amount but for a swap of a single pool
  /// @param params The params for the quote, encoded as `QuoteExactOutputSingleParams`
  /// tokenIn The token being swapped in
  /// tokenOut The token being swapped out
  /// fee The fee of the token pool to consider for the pair
  /// amountOut The desired output amount
  /// sqrtPriceLimitX96 The price limit of the pool that cannot be exceeded by the swap
  /// @return amountIn The amount required as the input for the swap in order to receive `amountOut`
  /// @return sqrtPriceX96After The sqrt price of the pool after the swap
  /// @return initializedTicksCrossed The number of initialized ticks that the swap crossed
  /// @return gasEstimate The estimate of the gas that the swap consumes
  function quoteExactOutputSingle(
    QuoteExactOutputSingleParams memory params
  ) external returns (uint256 amountIn, uint160 sqrtPriceX96After, uint32 initializedTicksCrossed, uint256 gasEstimate);
}

/// @title The interface for the Uniswap V3 Factory
/// @notice The Uniswap V3 Factory facilitates creation of Uniswap V3 pools and control over the protocol fees
interface IUniswapV3Factory {
  /// @notice Emitted when the owner of the factory is changed
  /// @param oldOwner The owner before the owner was changed
  /// @param newOwner The owner after the owner was changed
  event OwnerChanged(address indexed oldOwner, address indexed newOwner);

  /// @notice Emitted when a pool is created
  /// @param token0 The first token of the pool by address sort order
  /// @param token1 The second token of the pool by address sort order
  /// @param fee The fee collected upon every swap in the pool, denominated in hundredths of a bip
  /// @param tickSpacing The minimum number of ticks between initialized ticks
  /// @param pool The address of the created pool
  event PoolCreated(
    address indexed token0,
    address indexed token1,
    uint24 indexed fee,
    int24 tickSpacing,
    address pool
  );

  /// @notice Emitted when a new fee amount is enabled for pool creation via the factory
  /// @param fee The enabled fee, denominated in hundredths of a bip
  /// @param tickSpacing The minimum number of ticks between initialized ticks for pools created with the given fee
  event FeeAmountEnabled(uint24 indexed fee, int24 indexed tickSpacing);

  /// @notice Returns the current owner of the factory
  /// @dev Can be changed by the current owner via setOwner
  /// @return The address of the factory owner
  function owner() external view returns (address);

  /// @notice Returns the tick spacing for a given fee amount, if enabled, or 0 if not enabled
  /// @dev A fee amount can never be removed, so this value should be hard coded or cached in the calling context
  /// @param fee The enabled fee, denominated in hundredths of a bip. Returns 0 in case of unenabled fee
  /// @return The tick spacing
  function feeAmountTickSpacing(uint24 fee) external view returns (int24);

  /// @notice Returns the pool address for a given pair of tokens and a fee, or address 0 if it does not exist
  /// @dev tokenA and tokenB may be passed in either token0/token1 or token1/token0 order
  /// @param tokenA The contract address of either token0 or token1
  /// @param tokenB The contract address of the other token
  /// @param fee The fee collected upon every swap in the pool, denominated in hundredths of a bip
  /// @return pool The pool address
  function getPool(address tokenA, address tokenB, uint24 fee) external view returns (address pool);

  /// @notice Creates a pool for the given two tokens and fee
  /// @param tokenA One of the two tokens in the desired pool
  /// @param tokenB The other of the two tokens in the desired pool
  /// @param fee The desired fee for the pool
  /// @dev tokenA and tokenB may be passed in either order: token0/token1 or token1/token0. tickSpacing is retrieved
  /// from the fee. The call will revert if the pool already exists, the fee is invalid, or the token arguments
  /// are invalid.
  /// @return pool The address of the newly created pool
  function createPool(address tokenA, address tokenB, uint24 fee) external returns (address pool);

  /// @notice Updates the owner of the factory
  /// @dev Must be called by the current owner
  /// @param _owner The new owner of the factory
  function setOwner(address _owner) external;

  /// @notice Enables a fee amount with the given tickSpacing
  /// @dev Fee amounts may never be removed once enabled
  /// @param fee The fee amount to enable, denominated in hundredths of a bip (i.e. 1e-6)
  /// @param tickSpacing The spacing between ticks to be enforced for all pools created with the given fee amount
  function enableFeeAmount(uint24 fee, int24 tickSpacing) external;
}

/**
 * @title The interface for the Algebra Factory
 * @dev Credit to Uniswap Labs under GPL-2.0-or-later license:
 * https://github.com/Uniswap/v3-core/tree/main/contracts/interfaces
 */
interface IAlgebraFactory {
  /**
   * @notice Emitted when the owner of the factory is changed
   * @param newOwner The owner after the owner was changed
   */
  event Owner(address indexed newOwner);

  /**
   * @notice Emitted when the vault address is changed
   * @param newVaultAddress The vault address after the address was changed
   */
  event VaultAddress(address indexed newVaultAddress);

  /**
   * @notice Emitted when a pool is created
   * @param token0 The first token of the pool by address sort order
   * @param token1 The second token of the pool by address sort order
   * @param pool The address of the created pool
   */
  event Pool(address indexed token0, address indexed token1, address pool);

  /**
   * @notice Emitted when the farming address is changed
   * @param newFarmingAddress The farming address after the address was changed
   */
  event FarmingAddress(address indexed newFarmingAddress);

  /**
   * @notice Emitted when the default community fee is changed
   * @param newDefaultCommunityFee The new default community fee value
   */
  event DefaultCommunityFee(uint8 newDefaultCommunityFee);

  event FeeConfiguration(
    uint16 alpha1,
    uint16 alpha2,
    uint32 beta1,
    uint32 beta2,
    uint16 gamma1,
    uint16 gamma2,
    uint32 volumeBeta,
    uint16 volumeGamma,
    uint16 baseFee
  );

  /**
   * @notice Returns the current owner of the factory
   * @dev Can be changed by the current owner via setOwner
   * @return The address of the factory owner
   */
  function owner() external view returns (address);

  /**
   * @notice Returns the current poolDeployerAddress
   * @return The address of the poolDeployer
   */
  function poolDeployer() external view returns (address);

  /**
   * @dev Is retrieved from the pools to restrict calling
   * certain functions not by a tokenomics contract
   * @return The tokenomics contract address
   */
  function farmingAddress() external view returns (address);

  /**
   * @notice Returns the default community fee
   * @return Fee which will be set at the creation of the pool
   */
  function defaultCommunityFee() external view returns (uint8);

  function vaultAddress() external view returns (address);

  /**
   * @notice Returns the pool address for a given pair of tokens and a fee, or address 0 if it does not exist
   * @dev tokenA and tokenB may be passed in either token0/token1 or token1/token0 order
   * @param tokenA The contract address of either token0 or token1
   * @param tokenB The contract address of the other token
   * @return pool The pool address
   */
  function poolByPair(address tokenA, address tokenB) external view returns (address pool);

  /**
   * @notice Creates a pool for the given two tokens and fee
   * @param tokenA One of the two tokens in the desired pool
   * @param tokenB The other of the two tokens in the desired pool
   * @dev tokenA and tokenB may be passed in either order: token0/token1 or token1/token0. tickSpacing is retrieved
   * from the fee. The call will revert if the pool already exists, the fee is invalid, or the token arguments
   * are invalid.
   * @return pool The address of the newly created pool
   */
  function createPool(address tokenA, address tokenB) external returns (address pool);
}

/// @title Pool state that never changes
/// @notice These parameters are fixed for a pool forever, i.e., the methods will always return the same values
interface IUniswapV3PoolImmutables {
  /// @notice The contract that deployed the pool, which must adhere to the IUniswapV3Factory interface
  /// @return The contract address
  function factory() external view returns (address);

  /// @notice The first of the two tokens of the pool, sorted by address
  /// @return The token contract address
  function token0() external view returns (address);

  /// @notice The second of the two tokens of the pool, sorted by address
  /// @return The token contract address
  function token1() external view returns (address);

  /// @notice The pool's fee in hundredths of a bip, i.e. 1e-6
  /// @return The fee
  function fee() external view returns (uint24);

  /// @notice The pool tick spacing
  /// @dev Ticks can only be used at multiples of this value, minimum of 1 and always positive
  /// e.g.: a tickSpacing of 3 means ticks can be initialized every 3rd tick, i.e., ..., -6, -3, 0, 3, 6, ...
  /// This value is an int24 to avoid casting even though it is always positive.
  /// @return The tick spacing
  function tickSpacing() external view returns (int24);

  /// @notice The maximum amount of position liquidity that can use any tick in the range
  /// @dev This parameter is enforced per tick to prevent liquidity from overflowing a uint128 at any point, and
  /// also prevents out-of-range liquidity from being used to prevent adding in-range liquidity to a pool
  /// @return The max amount of liquidity per tick
  function maxLiquidityPerTick() external view returns (uint128);
}

/// @title Pool state that can change
/// @notice These methods compose the pool's state, and can change with any frequency including multiple times
/// per transaction
interface IUniswapV3PoolState {
  /// @notice The 0th storage slot in the pool stores many values, and is exposed as a single method to save gas
  /// when accessed externally.
  /// @return sqrtPriceX96 The current price of the pool as a sqrt(token1/token0) Q64.96 value
  /// tick The current tick of the pool, i.e. according to the last tick transition that was run.
  /// This value may not always be equal to SqrtTickMath.getTickAtSqrtRatio(sqrtPriceX96) if the price is on a tick
  /// boundary.
  /// observationIndex The index of the last oracle observation that was written,
  /// observationCardinality The current maximum number of observations stored in the pool,
  /// observationCardinalityNext The next maximum number of observations, to be updated when the observation.
  /// feeProtocol The protocol fee for both tokens of the pool.
  /// Encoded as two 4 bit values, where the protocol fee of token1 is shifted 4 bits and the protocol fee of token0
  /// is the lower 4 bits. Used as the denominator of a fraction of the swap fee, e.g. 4 means 1/4th of the swap fee.
  /// unlocked Whether the pool is currently locked to reentrancy
  function slot0()
    external
    view
    returns (
      uint160 sqrtPriceX96,
      int24 tick,
      uint16 observationIndex,
      uint16 observationCardinality,
      uint16 observationCardinalityNext,
      uint8 feeProtocol,
      bool unlocked
    );

  /// @notice The fee growth as a Q128.128 fees of token0 collected per unit of liquidity for the entire life of the pool
  /// @dev This value can overflow the uint256
  function feeGrowthGlobal0X128() external view returns (uint256);

  /// @notice The fee growth as a Q128.128 fees of token1 collected per unit of liquidity for the entire life of the pool
  /// @dev This value can overflow the uint256
  function feeGrowthGlobal1X128() external view returns (uint256);

  /// @notice The amounts of token0 and token1 that are owed to the protocol
  /// @dev Protocol fees will never exceed uint128 max in either token
  function protocolFees() external view returns (uint128 token0, uint128 token1);

  /// @notice The currently in range liquidity available to the pool
  /// @dev This value has no relationship to the total liquidity across all ticks
  function liquidity() external view returns (uint128);

  /// @notice Look up information about a specific tick in the pool
  /// @param tick The tick to look up
  /// @return liquidityGross the total amount of position liquidity that uses the pool either as tick lower or
  /// tick upper,
  /// liquidityNet how much liquidity changes when the pool price crosses the tick,
  /// feeGrowthOutside0X128 the fee growth on the other side of the tick from the current tick in token0,
  /// feeGrowthOutside1X128 the fee growth on the other side of the tick from the current tick in token1,
  /// tickCumulativeOutside the cumulative tick value on the other side of the tick from the current tick
  /// secondsPerLiquidityOutsideX128 the seconds spent per liquidity on the other side of the tick from the current tick,
  /// secondsOutside the seconds spent on the other side of the tick from the current tick,
  /// initialized Set to true if the tick is initialized, i.e. liquidityGross is greater than 0, otherwise equal to false.
  /// Outside values can only be used if the tick is initialized, i.e. if liquidityGross is greater than 0.
  /// In addition, these values are only relative and must be used only in comparison to previous snapshots for
  /// a specific position.
  function ticks(
    int24 tick
  )
    external
    view
    returns (
      uint128 liquidityGross,
      int128 liquidityNet,
      uint256 feeGrowthOutside0X128,
      uint256 feeGrowthOutside1X128,
      int56 tickCumulativeOutside,
      uint160 secondsPerLiquidityOutsideX128,
      uint32 secondsOutside,
      bool initialized
    );

  /// @notice Returns 256 packed tick initialized boolean values. See TickBitmap for more information
  function tickBitmap(int16 wordPosition) external view returns (uint256);

  /// @notice Returns the information about a position by the position's key
  /// @param key The position's key is a hash of a preimage composed by the owner, tickLower and tickUpper
  /// @return _liquidity The amount of liquidity in the position,
  /// Returns feeGrowthInside0LastX128 fee growth of token0 inside the tick range as of the last mint/burn/poke,
  /// Returns feeGrowthInside1LastX128 fee growth of token1 inside the tick range as of the last mint/burn/poke,
  /// Returns tokensOwed0 the computed amount of token0 owed to the position as of the last mint/burn/poke,
  /// Returns tokensOwed1 the computed amount of token1 owed to the position as of the last mint/burn/poke
  function positions(
    bytes32 key
  )
    external
    view
    returns (
      uint128 _liquidity,
      uint256 feeGrowthInside0LastX128,
      uint256 feeGrowthInside1LastX128,
      uint128 tokensOwed0,
      uint128 tokensOwed1
    );

  /// @notice Returns data about a specific observation index
  /// @param index The element of the observations array to fetch
  /// @dev You most likely want to use #observe() instead of this method to get an observation as of some amount of time
  /// ago, rather than at a specific index in the array.
  /// @return blockTimestamp The timestamp of the observation,
  /// Returns tickCumulative the tick multiplied by seconds elapsed for the life of the pool as of the observation timestamp,
  /// Returns secondsPerLiquidityCumulativeX128 the seconds per in range liquidity for the life of the pool as of the observation timestamp,
  /// Returns initialized whether the observation has been initialized and the values are safe to use
  function observations(
    uint256 index
  )
    external
    view
    returns (uint32 blockTimestamp, int56 tickCumulative, uint160 secondsPerLiquidityCumulativeX128, bool initialized);
}

interface IKodiakswapV3PoolState {
  /// @notice The 0th storage slot in the pool stores many values, and is exposed as a single method to save gas
  /// when accessed externally.
  /// @return sqrtPriceX96 The current price of the pool as a sqrt(token1/token0) Q64.96 value
  /// tick The current tick of the pool, i.e. according to the last tick transition that was run.
  /// This value may not always be equal to SqrtTickMath.getTickAtSqrtRatio(sqrtPriceX96) if the price is on a tick
  /// boundary.
  /// observationIndex The index of the last oracle observation that was written,
  /// observationCardinality The current maximum number of observations stored in the pool,
  /// observationCardinalityNext The next maximum number of observations, to be updated when the observation.
  /// feeProtocol The protocol fee for both tokens of the pool.
  /// Encoded as two 4 bit values, where the protocol fee of token1 is shifted 4 bits and the protocol fee of token0
  /// is the lower 4 bits. Used as the denominator of a fraction of the swap fee, e.g. 4 means 1/4th of the swap fee.
  /// unlocked Whether the pool is currently locked to reentrancy
  function slot0()
    external
    view
    returns (
      uint160 sqrtPriceX96,
      int24 tick,
      uint16 observationIndex,
      uint16 observationCardinality,
      uint16 observationCardinalityNext,
      uint32 feeProtocol,
      bool unlocked
    );

  /// @notice The fee growth as a Q128.128 fees of token0 collected per unit of liquidity for the entire life of the pool
  /// @dev This value can overflow the uint256
  function feeGrowthGlobal0X128() external view returns (uint256);

  /// @notice The fee growth as a Q128.128 fees of token1 collected per unit of liquidity for the entire life of the pool
  /// @dev This value can overflow the uint256
  function feeGrowthGlobal1X128() external view returns (uint256);

  /// @notice The amounts of token0 and token1 that are owed to the protocol
  /// @dev Protocol fees will never exceed uint128 max in either token
  function protocolFees() external view returns (uint128 token0, uint128 token1);

  /// @notice The currently in range liquidity available to the pool
  /// @dev This value has no relationship to the total liquidity across all ticks
  function liquidity() external view returns (uint128);

  /// @notice Look up information about a specific tick in the pool
  /// @param tick The tick to look up
  /// @return liquidityGross the total amount of position liquidity that uses the pool either as tick lower or
  /// tick upper,
  /// liquidityNet how much liquidity changes when the pool price crosses the tick,
  /// feeGrowthOutside0X128 the fee growth on the other side of the tick from the current tick in token0,
  /// feeGrowthOutside1X128 the fee growth on the other side of the tick from the current tick in token1,
  /// tickCumulativeOutside the cumulative tick value on the other side of the tick from the current tick
  /// secondsPerLiquidityOutsideX128 the seconds spent per liquidity on the other side of the tick from the current tick,
  /// secondsOutside the seconds spent on the other side of the tick from the current tick,
  /// initialized Set to true if the tick is initialized, i.e. liquidityGross is greater than 0, otherwise equal to false.
  /// Outside values can only be used if the tick is initialized, i.e. if liquidityGross is greater than 0.
  /// In addition, these values are only relative and must be used only in comparison to previous snapshots for
  /// a specific position.
  function ticks(
    int24 tick
  )
    external
    view
    returns (
      uint128 liquidityGross,
      int128 liquidityNet,
      uint256 feeGrowthOutside0X128,
      uint256 feeGrowthOutside1X128,
      int56 tickCumulativeOutside,
      uint160 secondsPerLiquidityOutsideX128,
      uint32 secondsOutside,
      bool initialized
    );

  /// @notice Returns 256 packed tick initialized boolean values. See TickBitmap for more information
  function tickBitmap(int16 wordPosition) external view returns (uint256);

  /// @notice Returns the information about a position by the position's key
  /// @param key The position's key is a hash of a preimage composed by the owner, tickLower and tickUpper
  /// @return _liquidity The amount of liquidity in the position,
  /// Returns feeGrowthInside0LastX128 fee growth of token0 inside the tick range as of the last mint/burn/poke,
  /// Returns feeGrowthInside1LastX128 fee growth of token1 inside the tick range as of the last mint/burn/poke,
  /// Returns tokensOwed0 the computed amount of token0 owed to the position as of the last mint/burn/poke,
  /// Returns tokensOwed1 the computed amount of token1 owed to the position as of the last mint/burn/poke
  function positions(
    bytes32 key
  )
    external
    view
    returns (
      uint128 _liquidity,
      uint256 feeGrowthInside0LastX128,
      uint256 feeGrowthInside1LastX128,
      uint128 tokensOwed0,
      uint128 tokensOwed1
    );

  /// @notice Returns data about a specific observation index
  /// @param index The element of the observations array to fetch
  /// @dev You most likely want to use #observe() instead of this method to get an observation as of some amount of time
  /// ago, rather than at a specific index in the array.
  /// @return blockTimestamp The timestamp of the observation,
  /// Returns tickCumulative the tick multiplied by seconds elapsed for the life of the pool as of the observation timestamp,
  /// Returns secondsPerLiquidityCumulativeX128 the seconds per in range liquidity for the life of the pool as of the observation timestamp,
  /// Returns initialized whether the observation has been initialized and the values are safe to use
  function observations(
    uint256 index
  )
    external
    view
    returns (uint32 blockTimestamp, int56 tickCumulative, uint160 secondsPerLiquidityCumulativeX128, bool initialized);
}

/// @title Pool state that is not stored
/// @notice Contains view functions to provide information about the pool that is computed rather than stored on the
/// blockchain. The functions here may have variable gas costs.
interface IUniswapV3PoolDerivedState {
  /// @notice Returns the cumulative tick and liquidity as of each timestamp `secondsAgo` from the current block timestamp
  /// @dev To get a time weighted average tick or liquidity-in-range, you must call this with two values, one representing
  /// the beginning of the period and another for the end of the period. E.g., to get the last hour time-weighted average tick,
  /// you must call it with secondsAgos = [3600, 0].
  /// @dev The time weighted average tick represents the geometric time weighted average price of the pool, in
  /// log base sqrt(1.0001) of token1 / token0. The TickMath library can be used to go from a tick value to a ratio.
  /// @param secondsAgos From how long ago each cumulative tick and liquidity value should be returned
  /// @return tickCumulatives Cumulative tick values as of each `secondsAgos` from the current block timestamp
  /// @return secondsPerLiquidityCumulativeX128s Cumulative seconds per liquidity-in-range value as of each `secondsAgos` from the current block
  /// timestamp
  function observe(
    uint32[] calldata secondsAgos
  ) external view returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s);

  /// @notice Returns a snapshot of the tick cumulative, seconds per liquidity and seconds inside a tick range
  /// @dev Snapshots must only be compared to other snapshots, taken over a period for which a position existed.
  /// I.e., snapshots cannot be compared if a position is not held for the entire period between when the first
  /// snapshot is taken and the second snapshot is taken.
  /// @param tickLower The lower tick of the range
  /// @param tickUpper The upper tick of the range
  /// @return tickCumulativeInside The snapshot of the tick accumulator for the range
  /// @return secondsPerLiquidityInsideX128 The snapshot of seconds per liquidity for the range
  /// @return secondsInside The snapshot of seconds per liquidity for the range
  function snapshotCumulativesInside(
    int24 tickLower,
    int24 tickUpper
  ) external view returns (int56 tickCumulativeInside, uint160 secondsPerLiquidityInsideX128, uint32 secondsInside);
}

/// @title The interface for a Uniswap V3 Pool
/// @notice A Uniswap pool facilitates swapping and automated market making between any two assets that strictly conform
/// to the ERC20 specification
/// @dev The pool interface is broken up into many smaller pieces
interface IUniswapV3Pool is IUniswapV3PoolImmutables, IUniswapV3PoolState, IUniswapV3PoolDerivedState {

}

interface IUniswapV3PoolWithUint32FeeProtocol is IUniswapV3PoolImmutables, IUniswapV3PoolDerivedState {
  function slot0()
    external
    view
    returns (
      uint160 sqrtPriceX96,
      int24 tick,
      uint16 observationIndex,
      uint16 observationCardinality,
      uint16 observationCardinalityNext,
      uint32 feeProtocol,
      bool unlocked
    );
}

File 12 of 18 : IDexType.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

interface IDexType {
  /// @notice Enum representing different DEX types
  enum DexType {
    UNISWAP_V2, // 0
    UNISWAP_V3, // 1
    SUSHISWAP_V2, // 2
    SUSHISWAP_V3, // 3
    CAMELOT_V3, // 4
    BEX, // 5
    KODIAK_V3, // 6
    KODIAK_V2, // 7
    STEER, // 8
    GAMMA // 9
  }

  /// @notice Emitted when the default DEX is set
  /// @param dex The DEX index
  event SetDefaultDex(uint8 dex);

  /// @notice Thrown when trying to use an unsupported DEX type
  error UnsupportedDexType();
}

File 13 of 18 : ILpRouter.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

import {IDexType} from "./IDexType.sol";

interface ILpRouter is IDexType {
  /// @notice Thrown when router arrays have mismatched lengths
  error InvalidRouterLength();

  /// @notice Thrown when router address is zero
  error ZeroRouterAddress();

  /// @notice Thrown when address is zero
  error ZeroAddress();

  /// @notice Thrown when a function is called by an address that isn't governance
  error NotGovernance();

  /// @notice Thrown when total ratio is zero
  error TotalRatioZero();

  /// @notice Thrown when pool type is not supported
  error UnsupportedBexPoolType();

  /// @notice Thrown when failed to parse pool type
  error FailedToParsePoolType();

  /// @notice Emitted when the governance is updated
  event GovernanceUpdated(address indexed oldGovernance, address indexed newGovernance);

  /// @notice Emitted when a router is set for a DEX
  /// @param dex The DEX index
  /// @param router The router address
  event SetRouter(uint8 dex, address indexed router);

  /// @notice Emitted when a swap router is set
  /// @param swapRouter The swap router address
  event SetSwapRouter(address indexed old, address indexed swapRouter);

  struct LiquidityAddInfo {
    address lp;
    address token0;
    address token1;
    uint256 amount0;
    uint256 amount1;
  }

  struct LiquidityRemoveInfo {
    address lp;
    uint256 lpAmount;
    address token0;
    address token1;
    address recipient;
  }

  function addLiquidity(
    address lp,
    address tokenIn,
    uint256 amountIn,
    address recipient,
    DexType dexType
  ) external returns (uint256 lpAmountOut);

  function removeLiquidity(
    address lp,
    uint256 lpAmount,
    address recipient,
    address tokenOut,
    DexType dexType
  ) external returns (uint256 tokenOutAmount);
}

File 14 of 18 : ISwapRouter.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

import {IDexType} from "./IDexType.sol";

import {IBeraPool} from "./exchange/Beraswap.sol";

/**
 * @title ISwapRouter
 * @notice Interface for the SwapRouter contract that handles token swaps across multiple DEXes
 */
interface ISwapRouter is IDexType {
  // Errors
  /// @notice Thrown when router arrays have mismatched lengths
  error InvalidRouterLength();
  /// @notice Thrown when router/factory arrays have mismatched lengths
  error InvalidFactoryLength();
  /// @notice Thrown when path length is less than 2
  error InvalidPathLength();
  /// @notice Thrown when address is zero
  error ZeroAddress();
  /// @notice Thrown when router address is zero
  error ZeroRouterAddress();
  /// @notice Thrown when trying to use an unsupported factory
  error FactoryNotSupported();
  /// @notice Thrown when trying to use an unsupported router
  error RouterNotSupported();
  /// @notice Thrown when no pool is found for a multihop swap
  error NoPoolFoundForMultihopSwap();
  /// @notice Thrown when no pool is found for a multihop quote
  error NoPoolFoundForMultihopQuote();
  /// @notice Thrown when output amount is less than minimum
  /// @param amountOut Actual output amount
  /// @param amountOutMinimum Minimum required output amount
  error InsufficientOutputAmount(uint256 amountOut, uint256 amountOutMinimum);
  /// @notice Thrown when amount is zero
  error ZeroAmount();
  /// @notice Thrown when a function is called by an address that isn't governance
  error NotGovernance();
  /// @notice Thrown when no swap route is found
  error NoSwapRouteFound();
  /// @notice Thrown when path length exceeds the maximum
  error PathLengthExceeded();
  /// @notice Thrown when no pool is found for a token pair
  error NoPoolFound();

  // Events
  /// @notice Emitted when the governance is updated
  event GovernanceUpdated(address indexed oldGovernance, address indexed newGovernance);
  /// @notice Emitted when a router is set for a DEX
  /// @param dex The DEX index
  /// @param router The router address
  event SetRouter(uint8 dex, address indexed router);
  /// @notice Emitted when a factory is set for a DEX
  /// @param dex The DEX index
  /// @param factory The factory address
  event SetFactory(uint8 dex, address indexed factory);
  /// @notice Emitted when a pool is set
  /// @param tokenIn The input token address
  /// @param tokenOut The output token address
  /// @param pool The pool address
  event SetPool(address indexed tokenIn, address indexed tokenOut, address pool);
  /// @notice Emitted when a swap route is set
  /// @param tokenIn The input token address
  /// @param tokenOut The output token address
  /// @param path The swap route path
  /// @param reversePath Whether to set the inverse swap route
  event SetSwapRoute(address indexed tokenIn, address indexed tokenOut, SwapRoutePath[] path, bool reversePath);
  /// @notice Emitted when a DEX pool fee denominator is set
  /// @param dex The DEX index
  /// @param feeDenominator The pool fee denominator
  event SetDexFeeDenominator(uint8 dex, uint256 feeDenominator);

  /// @notice Struct for a swap route path
  /// @param tokenIn The input token address
  /// @param tokenOut The output token address
  /// @param dex The DEX type
  /// @param isMultiPath Whether the path is a multi-path swap
  /// @param pool The pool address
  struct SwapRoutePath {
    address tokenIn;
    address tokenOut;
    DexType dex;
    bool isMultiPath;
    address pool;
  }

  // Functions
  /// @notice Returns the wrapped native token address (e.g. WETH)
  /// @return The address of the wrapped native token
  function wrappedNative() external view returns (address);

  /// @notice Returns the router address for a given DEX
  /// @param dex The DEX index
  /// @return The router address
  function routers(uint8 dex) external view returns (address);

  /// @notice Returns the factory address for a given DEX
  /// @param dex The DEX index
  /// @return The factory address
  function factories(uint8 dex) external view returns (address);

  /// @notice Returns the default DEX type used for swaps
  /// @return The default DexType enum value
  function defaultDex() external view returns (DexType);

  /// @notice Swaps tokens using the default DEX
  /// @param tokenIn The input token address
  /// @param tokenOut The output token address
  /// @param amountIn The amount of input tokens
  /// @param amountOutMinimum The minimum amount of output tokens required
  /// @param recipient The address that will receive the output tokens
  /// @return amountOut The amount of output tokens received
  function swapWithDefaultDex(
    address tokenIn,
    address tokenOut,
    uint256 amountIn,
    uint256 amountOutMinimum,
    address recipient
  ) external returns (uint256 amountOut);

  /// @notice Swaps tokens using a specified DEX
  /// @param tokenIn The input token address
  /// @param tokenOut The output token address
  /// @param amountIn The amount of input tokens
  /// @param amountOutMinimum The minimum amount of output tokens required
  /// @param recipient The address that will receive the output tokens
  /// @param dex The DEX to use for the swap
  /// @return amountOut The amount of output tokens received
  function swap(
    address tokenIn,
    address tokenOut,
    uint256 amountIn,
    uint256 amountOutMinimum,
    address recipient,
    DexType dex
  ) external returns (uint256 amountOut);

  /// @notice Performs a multi-hop swap using the default DEX
  /// @param path Array of token addresses representing the swap path
  /// @param amountIn The amount of input tokens
  /// @param amountOutMinimum The minimum amount of output tokens required
  /// @param recipient The address that will receive the output tokens
  /// @return amountOut The amount of output tokens received
  function swapWithPathWithDefaultDex(
    address[] calldata path,
    uint256 amountIn,
    uint256 amountOutMinimum,
    address recipient
  ) external returns (uint256 amountOut);

  /// @notice Performs a multi-hop swap using a specified DEX
  /// @param path Array of token addresses representing the swap path
  /// @param amountIn The amount of input tokens
  /// @param amountOutMinimum The minimum amount of output tokens required
  /// @param recipient The address that will receive the output tokens
  /// @param dex The DEX to use for the swap
  /// @return amountOut The amount of output tokens received
  function swapWithPath(
    address[] calldata path,
    uint256 amountIn,
    uint256 amountOutMinimum,
    address recipient,
    DexType dex
  ) external returns (uint256 amountOut);

  /// @notice Gets a quote for a swap using the default DEX
  /// @param tokenIn The input token address
  /// @param tokenOut The output token address
  /// @param amountIn The amount of input tokens
  /// @return amountOut The expected amount of output tokens
  function getQuoteWithDefaultDex(
    address tokenIn,
    address tokenOut,
    uint256 amountIn
  ) external returns (uint256 amountOut);

  /// @notice Gets a quote for a swap using a specified DEX
  /// @param tokenIn The input token address
  /// @param tokenOut The output token address
  /// @param amountIn The amount of input tokens
  /// @param dex The DEX to use for the quote
  /// @return amountOut The expected amount of output tokens
  function getQuote(
    address tokenIn,
    address tokenOut,
    uint256 amountIn,
    DexType dex
  ) external returns (uint256 amountOut);

  /// @notice Gets a quote for a multi-hop swap using the default DEX
  /// @param path Array of token addresses representing the swap path
  /// @param amountIn The amount of input tokens
  /// @return amountOut The expected amount of output tokens
  function getQuoteWithPathWithDefaultDex(
    address[] memory path,
    uint256 amountIn
  ) external view returns (uint256 amountOut);

  /// @notice Gets a quote for a multi-hop swap using a specified DEX
  /// @param path Array of token addresses representing the swap path
  /// @param amountIn The amount of input tokens
  /// @param dex The DEX to use for the quote
  /// @return amountOut The expected amount of output tokens
  function getQuoteWithPath(
    address[] memory path,
    uint256 amountIn,
    DexType dex
  ) external view returns (uint256 amountOut);
}

File 15 of 18 : IVault.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

import {IERC4626} from "@openzeppelin/contracts/interfaces/IERC4626.sol";

/**
 * @title IVault
 * @notice Interface for Contrax Vault functionality
 */
interface IVault is IERC4626 {
  // Errors
  /// @notice Thrown when attempting to set a zero address
  error ZeroAddress();
  /// @notice Thrown when a caller is not the governance address
  error NotGovernance();
  /// @notice Thrown when a caller is not the timelock address
  error NotTimelock();
  /// @notice Thrown when a caller is not the controller address
  error NotController();
  /// @notice Thrown when attempting to set min ratio higher than max
  error MinGreaterThanMax();
  /// @notice Thrown when attempting to harvest the vault's underlying asset
  error CannotHarvestAsset();
  /// @notice Thrown when deposit results in fewer shares than minimum specified
  error InsufficientOutputShares(uint256 shares, uint256 minShares);
  /// @notice Thrown when redemption results in fewer assets than minimum specified
  error InsufficientOutputAssets(uint256 assets, uint256 minAssets);
  /// @notice Thrown when a fee is set that is greater than the maximum allowed
  error FeeTooHigh(uint16 fee, uint16 maxFee);
  /// @notice Thrown when there are no funds to earn
  error NoFundsToEarn();

  // Events
  /// @notice Emitted when the governance address is updated
  /// @param oldGovernance The old governance address
  /// @param newGovernance The new governance address
  event GovernanceChanged(address indexed oldGovernance, address indexed newGovernance);

  /// @notice Emitted when the fee recipient address is updated
  /// @param oldFeeRecipient The old fee recipient address
  /// @param newFeeRecipient The new fee recipient address
  event FeeRecipientChanged(address indexed oldFeeRecipient, address indexed newFeeRecipient);
  /// @notice Emitted when the timelock address is updated
  /// @param oldTimelock The old timelock address
  /// @param newTimelock The new timelock address
  event TimelockChanged(address indexed oldTimelock, address indexed newTimelock);
  /// @notice Emitted when the controller address is updated
  /// @param oldController The old controller address
  /// @param newController The new controller address
  event ControllerChanged(address indexed oldController, address indexed newController);
  /// @notice Emitted when the minimum ratio is updated
  /// @param oldMin The old minimum ratio
  /// @param newMin The new minimum ratio
  event MinChanged(uint256 oldMin, uint256 newMin);
  /// @notice Emitted when the deposit fee is updated
  /// @param oldFee The old deposit fee
  /// @param newFee The new deposit fee
  event DepositFeeChanged(uint16 oldFee, uint16 newFee);
  /// @notice Emitted when the withdraw fee is updated
  /// @param oldFee The old withdraw fee
  /// @param newFee The new withdraw fee
  event WithdrawFeeChanged(uint16 oldFee, uint16 newFee);

  // Functions
  /// @notice Sends available assets to controller to be invested in strategy
  function earn() external;

  /// @notice Calculates amount of assets available to be sent to strategy
  /// @return uint256 Amount of assets available
  function available() external view returns (uint256);

  /// @notice Deposits assets with minimum shares check
  /// @param assets Amount of assets to deposit
  /// @param receiver Address receiving the shares
  /// @param minShares Minimum shares that must be minted
  /// @return shares Amount of shares minted
  function deposit(uint256 assets, address receiver, uint256 minShares) external returns (uint256 shares);

  /// @notice Redeems shares with minimum assets check
  /// @param shares Amount of shares to redeem
  /// @param receiver Address receiving the assets
  /// @param owner Address that owns the shares
  /// @param minAssets Minimum assets that must be returned
  /// @return assets Amount of assets returned
  function redeem(uint256 shares, address receiver, address owner, uint256 minAssets) external returns (uint256 assets);
}

File 16 of 18 : IZapper.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {IWETH} from "../interfaces/exchange/IWETH.sol";
import {ISwapRouter} from "../interfaces/ISwapRouter.sol";
import {IVault} from "../interfaces/IVault.sol";

/**
 * @title IZapper Interface
 * @notice Interface for contracts that enable deposits/withdrawals from vaults using any token
 * @dev Implements functionality for converting tokens and interacting with vaults
 */
interface IZapper {
  // Structs
  /**
   * @notice Struct to track leftover tokens returned to users
   * @param tokens Address of the token
   * @param amounts Amount of tokens returned
   */
  struct ReturnedAsset {
    address tokens;
    uint256 amounts;
  }

  // Errors
  /// @notice Thrown when a non-governance address calls a governance-only function
  error NotGovernance();
  /// @notice Thrown when a zero address is provided
  error ZeroAddress();
  /// @notice Thrown when input amount is below minimum required
  error InsufficientInputAmount(address token, uint256 provided, uint256 minimum);
  /// @notice Thrown when token approval is missing
  error TokenNotApproved();
  /// @notice Thrown when ETH transfer fails
  error ETHTransferFailed();
  /// @notice Thrown when output amount is below minimum required
  error InsufficientOutputAmount(address token, uint256 received, uint256 minimum);
  /// @notice Thrown when fee percentage is too high
  error FeeTooHigh(uint16 fee, uint16 maxFee);
  /// @notice Thrown when total ratio is zero
  error TotalRatioZero();

  // Events
  /// @notice Emitted when swap router address is updated
  /// @param newSwapRouter Address of the new swap router
  /// @param oldSwapRouter Address of the previous swap router
  event SwapRouterChanged(address indexed newSwapRouter, address indexed oldSwapRouter);
  /// @notice Emitted when stablecoin address is updated
  /// @param newStableCoin Address of the new stablecoin
  /// @param oldStableCoin Address of the previous stablecoin
  event StableCoinChanged(address indexed newStableCoin, address indexed oldStableCoin);
  /// @notice Emitted when governance address is updated
  /// @param newGovernance Address of the new governance
  /// @param oldGovernance Address of the previous governance
  event GovernanceChanged(address indexed newGovernance, address indexed oldGovernance);
  /// @notice Emitted when lp router address is updated
  /// @param newLpRouter Address of the new lp router
  /// @param oldLpRouter Address of the previous lp router
  event LpRouterChanged(address indexed newLpRouter, address indexed oldLpRouter);
  /// @notice Emitted when zapIn fee is updated
  /// @param oldFee Previous fee percentage in basis points
  /// @param newFee New fee percentage in basis points
  event ZapInFeeChanged(uint16 oldFee, uint16 newFee);
  /// @notice Emitted when zapOut fee is updated
  /// @param oldFee Previous fee percentage in basis points
  /// @param newFee New fee percentage in basis points
  event ZapOutFeeChanged(uint16 oldFee, uint16 newFee);
  /// @notice Emitted when fee recipient is updated
  /// @param oldRecipient Address of the previous fee recipient
  /// @param newRecipient Address of the new fee recipient
  event FeeRecipientChanged(address indexed oldRecipient, address indexed newRecipient);
  /// @notice Emitted when tokens are deposited into a vault
  /// @param user Address of the depositor
  /// @param vault Address of the target vault
  /// @param tokenIn Address of the input token
  /// @param tokenInAmount Amount of input tokens
  /// @param assetsIn Amount of assets deposited
  /// @param shares Amount of vault shares received
  /// @param fee Amount of fee paid
  /// @param returnedAssets Array of any remaining tokens returned to caller
  event ZapIn(
    address indexed user,
    address indexed vault,
    address tokenIn,
    uint256 tokenInAmount,
    uint256 assetsIn,
    uint256 shares,
    uint256 fee,
    ReturnedAsset[] returnedAssets
  );
  /// @notice Emitted when tokens are withdrawn from a vault
  /// @param user Address of the withdrawer
  /// @param vault Address of the source vault
  /// @param tokenOut Address of the output token
  /// @param tokenOutAmount Amount of output tokens
  /// @param assetsOut Amount of assets withdrawn
  /// @param shares Amount of vault shares withdrawn
  /// @param fee Amount of fee paid
  /// @param returnedAssets Array of any remaining tokens returned to caller
  event ZapOut(
    address indexed user,
    address indexed vault,
    address tokenOut,
    uint256 tokenOutAmount,
    uint256 assetsOut,
    uint256 shares,
    uint256 fee,
    ReturnedAsset[] returnedAssets
  );

  /**
   * @notice Converts input token balance of address(this) to vault's desired token
   * @param vault The vault to deposit into
   * @param tokenIn The input token to convert
   * @return assetsOut Amount of converted assets
   * @return returnedAssets Array of any remaining tokens returned to caller
   */
  function swapToAssets(
    address vault,
    address tokenIn,
    uint256 tokenInAmount,
    address recipient
  ) external returns (uint256 assetsOut, ReturnedAsset[] memory returnedAssets);

  /**
   * @notice Converts vault's desired token balance to output token
   * @param vault The vault to withdraw from
   * @param tokenOut The output token to convert
   * @return tokenOutAmount Amount of converted tokens
   * @return returnedAssets Array of any remaining tokens returned to caller
   */
  function swapFromAssets(
    address vault,
    address tokenOut,
    uint256 assetsInAmount,
    address recipient
  ) external returns (uint256 tokenOutAmount, ReturnedAsset[] memory returnedAssets);

  /// @notice Deposits tokens into a vault after converting them if necessary
  /// @param vault Target vault
  /// @param tokenIn Input token address
  /// @param tokenInAmount Amount of input tokens
  /// @param minShares Minimum amount of vault shares to receive
  /// @return shares Number of vault shares received
  /// @return returnedAssets Array of any remaining tokens returned to caller
  function zapIn(
    IVault vault,
    address tokenIn,
    uint256 tokenInAmount,
    uint256 minShares
  ) external payable returns (uint256 shares, ReturnedAsset[] memory returnedAssets);

  /// @notice Withdraws from vault and converts to desired token
  /// @param vault Source vault
  /// @param withdrawAmount Amount of vault shares to withdraw
  /// @param tokenOut Desired output token
  /// @param minTokenOutAmount Minimum amount of desired tokens to receive
  /// @return tokenOutAmount Amount of output tokens received
  /// @return returnedAssets Array of any remaining tokens returned to caller
  function zapOut(
    IVault vault,
    uint256 withdrawAmount,
    address tokenOut,
    uint256 minTokenOutAmount
  ) external returns (uint256 tokenOutAmount, ReturnedAsset[] memory returnedAssets);
}

File 17 of 18 : IKodiak.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.20;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IUniswapV3Pool} from "../exchange/UniswapV3.sol";

interface IKodiakVaultV1 {
  function mint(
    uint256 mintAmount,
    address receiver
  ) external returns (uint256 amount0, uint256 amount1, uint128 liquidityMinted);

  function burn(
    uint256 burnAmount,
    address receiver
  ) external returns (uint256 amount0, uint256 amount1, uint128 liquidityBurned);

  function getMintAmounts(
    uint256 amount0Max,
    uint256 amount1Max
  ) external view returns (uint256 amount0, uint256 amount1, uint256 mintAmount);

  function getUnderlyingBalances() external view returns (uint256 amount0, uint256 amount1);

  function getPositionID() external view returns (bytes32 positionID);

  function token0() external view returns (address);

  function token1() external view returns (address);

  function upperTick() external view returns (int24);

  function lowerTick() external view returns (int24);

  function pool() external view returns (IUniswapV3Pool);

  function totalSupply() external view returns (uint256);

  function balanceOf(address account) external view returns (uint256);
}

interface IGauge {
  function deposit(uint256 amount, address account) external;

  function withdraw(uint256 amount) external;

  // solhint-disable-next-line func-name-mixedcase
  function claim_rewards(address account) external;

  // solhint-disable-next-line func-name-mixedcase
  function staking_token() external returns (address);
}

interface IXKdkTokenUsage {
  function allocate(address userAddress, uint256 amount, bytes calldata data) external;

  function deallocate(address userAddress, uint256 amount, bytes calldata data) external;
}

interface IxKodiak {
  struct RedeemInfo {
    uint256 kdkAmount; // KDK amount to receive when vesting has ended
    uint256 xKodiakAmount; // xKDK amount to redeem
    uint256 endTime;
    IXKdkTokenUsage rewardsAddress;
    uint256 rewardsAllocation; // Share of redeeming xKDK to allocate to the Rewards Usage contract
  }

  function getUserRedeemsLength(address userAddress) external view returns (uint256);

  function redeem(uint256 xKdkAmount, uint256 duration) external;

  function finalizeRedeem(uint256 redeemIndex) external;

  function getUserRedeem(
    address userAddress,
    uint256 redeemIndex
  )
    external
    view
    returns (
      uint256 kdkAmount,
      uint256 xKdkAmount,
      uint256 endTime,
      address rewardsContract,
      uint256 rewardsAllocation
    );
}

interface IKodiakV1RouterStaking {
  function addLiquidity(
    IKodiakVaultV1 pool,
    uint256 amount0Max,
    uint256 amount1Max,
    uint256 amount0Min,
    uint256 amount1Min,
    uint256 amountSharesMin,
    address receiver
  ) external returns (uint256 amount0, uint256 amount1, uint256 mintAmount);

  function addLiquidityETH(
    IKodiakVaultV1 pool,
    uint256 amount0Max,
    uint256 amount1Max,
    uint256 amount0Min,
    uint256 amount1Min,
    uint256 amountSharesMin,
    address receiver
  ) external payable returns (uint256 amount0, uint256 amount1, uint256 mintAmount);

  function addLiquidityAndStake(
    IGauge gauge,
    uint256 amount0Max,
    uint256 amount1Max,
    uint256 amount0Min,
    uint256 amount1Min,
    uint256 amountSharesMin,
    address receiver
  ) external returns (uint256 amount0, uint256 amount1, uint256 mintAmount);

  function addLiquidityETHAndStake(
    IGauge gauge,
    uint256 amount0Max,
    uint256 amount1Max,
    uint256 amount0Min,
    uint256 amount1Min,
    uint256 amountSharesMin,
    address receiver
  ) external payable returns (uint256 amount0, uint256 amount1, uint256 mintAmount);

  function removeLiquidity(
    IKodiakVaultV1 pool,
    uint256 burnAmount,
    uint256 amount0Min,
    uint256 amount1Min,
    address receiver
  ) external returns (uint256 amount0, uint256 amount1, uint128 liquidityBurned);

  function removeLiquidityETH(
    IKodiakVaultV1 pool,
    uint256 burnAmount,
    uint256 amount0Min,
    uint256 amount1Min,
    address payable receiver
  ) external returns (uint256 amount0, uint256 amount1, uint128 liquidityBurned);

  function removeLiquidityAndUnstake(
    IGauge gauge,
    uint256 burnAmount,
    uint256 amount0Min,
    uint256 amount1Min,
    address receiver
  ) external returns (uint256 amount0, uint256 amount1, uint128 liquidityBurned);

  function removeLiquidityETHAndUnstake(
    IGauge gauge,
    uint256 burnAmount,
    uint256 amount0Min,
    uint256 amount1Min,
    address payable receiver
  ) external returns (uint256 amount0, uint256 amount1, uint128 liquidityBurned);
}

File 18 of 18 : ZapperBase.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {IWETH} from "../interfaces/exchange/IWETH.sol";
import {ISwapRouter} from "../interfaces/ISwapRouter.sol";
import {ILpRouter} from "../interfaces/ILpRouter.sol";
import {IVault} from "../interfaces/IVault.sol";
import {IZapper} from "../interfaces/IZapper.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";

/**
 * @title ZapperBase
 * @notice Base contract for implementing zapper functionality that allows users to deposit/withdraw
 * from vaults using any token through automated swaps
 * @dev This is an abstract contract that should be inherited by specific zapper implementations
 */
abstract contract ZapperBase is ReentrancyGuard, IZapper {
  using Address for address;
  using SafeERC20 for IERC20;
  using SafeERC20 for IVault;

  /// @notice Minimum token amount required for zap operations to prevent dust transactions
  uint16 public constant MINIMUM_AMOUNT = 1000;

  /// @notice Maximum fee that can be set (10% = 1000 basis points)
  uint16 public constant MAX_FEE = 1000;

  /// @notice Maximum fee that can be set in basis points (100% = 10000 basis points)
  uint16 public constant MAX_FEE_BPS = 10000;

  /// @notice The wrapped version of the native token (e.g., WBERA)
  IWETH public immutable wrappedNative;

  /// @notice The fee percentage for zapIn operations in basis points (1/10000)
  uint16 public zapInFee;

  /// @notice The fee percentage for zapOut operations in basis points (1/10000)
  uint16 public zapOutFee;

  /// @notice The stablecoin token contract used for price calculations and intermediate swaps
  IERC20 public stablecoin;

  /// @notice Address of the contract administrator with special privileges
  address public governance;

  /// @notice Router contract used for token swaps
  ISwapRouter public swapRouter;

  /// @notice Router contract used for add or remove Liquidity
  ILpRouter public lpRouter;

  /// @notice Address that receives the fees
  address public feeRecipient;

  /**
   * @notice Constructs the ZapperBase contract
   * @param devAddress Address of the contract administrator
   * @param wrappedNativeAddress Address of the wrapped native token (e.g. WBERA)
   * @param stablecoinAddress Address of the stablecoin token
   * @param swapRouterAddress Address of the swap router contract
   * @param lpRouterAddress Address of the lp router contract
   * @param feeRecipientAddress Address that receives the fees
   * @param initialZapInFee Initial zapIn fee percentage in basis points
   * @param initialZapOutFee Initial zapOut fee percentage in basis points
   */
  constructor(
    address devAddress,
    address wrappedNativeAddress,
    address stablecoinAddress,
    address swapRouterAddress,
    address lpRouterAddress,
    address feeRecipientAddress,
    uint16 initialZapInFee,
    uint16 initialZapOutFee
  ) {
    if (
      devAddress == address(0) ||
      wrappedNativeAddress == address(0) ||
      stablecoinAddress == address(0) ||
      swapRouterAddress == address(0) ||
      lpRouterAddress == address(0) ||
      feeRecipientAddress == address(0)
    ) revert ZeroAddress();

    governance = devAddress;
    wrappedNative = IWETH(wrappedNativeAddress);

    wrappedNative.deposit{value: 0}();
    wrappedNative.withdraw(0);

    stablecoin = IERC20(stablecoinAddress);
    swapRouter = ISwapRouter(swapRouterAddress);
    lpRouter = ILpRouter(lpRouterAddress);

    if (initialZapInFee > MAX_FEE) revert FeeTooHigh(initialZapInFee, MAX_FEE);
    if (initialZapOutFee > MAX_FEE) revert FeeTooHigh(initialZapOutFee, MAX_FEE);

    feeRecipient = feeRecipientAddress;
    zapInFee = initialZapInFee;
    zapOutFee = initialZapOutFee;
  }

  /// @notice Restricts function access to only the governance address
  modifier onlyGovernance() {
    if (msg.sender != governance) revert NotGovernance();
    _;
  }

  /**
   * @notice Updates the governance address, only callable by governance
   * @param governanceAddress New governance address
   */
  function setGovernance(address governanceAddress) external onlyGovernance {
    _revertAddressZero(governanceAddress);
    governance = governanceAddress;
    emit GovernanceChanged(governanceAddress, governance);
  }

  /**
   * @notice Updates the swap router address, only callable by governance
   * @param routerAddress New swap router address
   */
  function setSwapRouter(address routerAddress) external onlyGovernance {
    _revertAddressZero(routerAddress);
    address old = address(swapRouter);
    swapRouter = ISwapRouter(routerAddress);
    emit SwapRouterChanged(old, routerAddress);
  }

  /**
   * @notice Updates the lp router address, only callable by governance
   * @param routerAddress New lp router address
   */
  function setLpRouter(address routerAddress) external onlyGovernance {
    _revertAddressZero(routerAddress);
    address old = address(lpRouter);
    lpRouter = ILpRouter(routerAddress);
    emit LpRouterChanged(old, routerAddress);
  }

  /**
   * @notice Updates the stablecoin address, only callable by governance
   * @param stablecoinAddress New stablecoin address
   */
  function setStableCoin(address stablecoinAddress) external onlyGovernance {
    _revertAddressZero(stablecoinAddress);
    stablecoin = IERC20(stablecoinAddress);
    emit StableCoinChanged(address(stablecoin), stablecoinAddress);
  }

  /**
   * @notice Updates the zapIn fee percentage, only callable by governance
   * @param newFee New fee percentage in basis points
   */
  function setZapInFee(uint16 newFee) external onlyGovernance {
    if (newFee > MAX_FEE) revert FeeTooHigh(newFee, MAX_FEE);
    uint16 oldFee = zapInFee;
    zapInFee = newFee;
    emit ZapInFeeChanged(oldFee, newFee);
  }

  /**
   * @notice Updates the zapOut fee percentage, only callable by governance
   * @param newFee New fee percentage in basis points
   */
  function setZapOutFee(uint16 newFee) external onlyGovernance {
    if (newFee > MAX_FEE) revert FeeTooHigh(newFee, MAX_FEE);
    uint16 oldFee = zapOutFee;
    zapOutFee = newFee;
    emit ZapOutFeeChanged(oldFee, newFee);
  }

  /**
   * @notice Updates the fee recipient address, only callable by governance
   * @param newRecipient New address to receive fees
   */
  function setFeeRecipient(address newRecipient) external onlyGovernance {
    _revertAddressZero(newRecipient);
    address oldRecipient = feeRecipient;
    feeRecipient = newRecipient;
    emit FeeRecipientChanged(oldRecipient, newRecipient);
  }

  receive() external payable {
    assert(msg.sender == address(wrappedNative));
  }

  /** Internal functions */

  function _revertAddressZero(address addressToCheck) internal pure {
    if (addressToCheck == address(0)) revert ZeroAddress();
  }

  /**
   * @notice Approves spending of a token if not already approved
   * @param tokenAddress Token to approve
   * @param spenderAddress Address to approve spending for
   */
  function _approveTokenIfNeeded(address tokenAddress, address spenderAddress) internal {
    if (IERC20(tokenAddress).allowance(address(this), spenderAddress) == 0) {
      IERC20(tokenAddress).approve(spenderAddress, type(uint256).max);
    }
  }

  function _safeTransferFromTokens(address token, uint256 amount) internal returns (uint256) {
    if (IERC20(token).allowance(msg.sender, address(this)) < amount) revert TokenNotApproved();
    uint256 balanceBefore = IERC20(token).balanceOf(address(this));
    IERC20(token).safeTransferFrom(msg.sender, address(this), amount);
    uint256 balanceAfter = IERC20(token).balanceOf(address(this));
    return balanceAfter - balanceBefore;
  }

  /**
   * @notice Divides an amount between two tokens based on their ratio
   * @dev Used to split a single token amount into two token amounts while maintaining their ratio
   * @param amount The total amount to divide
   * @param ratio0 The ratio for the first token
   * @param ratio1 The ratio for the second token
   * @return amount0 The amount allocated to the first token
   * @return amount1 The amount allocated to the second token
   */
  function _divideAmountInRatio(
    uint256 amount,
    uint256 ratio0,
    uint256 ratio1
  ) internal pure returns (uint256, uint256) {
    uint256 totalRatio = ratio0 + ratio1;
    if (totalRatio == 0) revert TotalRatioZero();
    uint256 amount0 = (amount * ratio0) / totalRatio;
    uint256 amount1 = amount - amount0;
    return (amount0, amount1);
  }

  /**
   * @notice Returns any remaining tokens to the caller
   * @param tokens Array of token addresses to check and return
   * @return returnedAssets Array of ReturnedAsset structs containing token addresses and amounts returned
   */
  function _returnAssets(
    address[] memory tokens,
    address recipient
  ) internal returns (ReturnedAsset[] memory returnedAssets) {
    uint256 balance;

    returnedAssets = new ReturnedAsset[](tokens.length);
    for (uint256 i = 0; i < tokens.length; i++) {
      if (tokens[i] == address(0)) continue;
      balance = IERC20(tokens[i]).balanceOf(address(this));
      returnedAssets[i] = ReturnedAsset({tokens: tokens[i], amounts: balance});
      if (balance > 0) {
        if (tokens[i] == address(wrappedNative)) {
          wrappedNative.withdraw(balance);
          (bool success, ) = recipient.call{value: balance}(new bytes(0));
          if (!success) revert ETHTransferFailed();
        } else {
          IERC20(tokens[i]).safeTransfer(recipient, balance);
        }
      }
    }
  }

  /**
   * @notice Returns any remaining tokens to the caller
   * @param token The token to return
   * @param recipient The address to return the token to
   * @return returnedAssets Array of ReturnedAsset structs containing token addresses and amounts returned
   */
  function _returnAsset(address token, address recipient) internal returns (ReturnedAsset[] memory returnedAssets) {
    address[] memory tokens = new address[](1);
    tokens[0] = token;
    returnedAssets = _returnAssets(tokens, recipient);
  }

  /**
   * @notice Returns the amount of tokens to the caller, if the token is wrappedNative, it will be converted to native token
   * @param token The token to return
   * @param recipient The address to return the token to
   * @param amount The amount of tokens to return
   */
  function _returnAssetWithAmount(address token, address recipient, uint256 amount) internal {
    if (amount > 0) {
      if (token == address(wrappedNative)) {
        wrappedNative.withdraw(amount);
        (bool success, ) = recipient.call{value: amount}(new bytes(0));
        if (!success) revert ETHTransferFailed();
      } else {
        IERC20(token).safeTransfer(recipient, amount);
      }
    }
  }

  /**
   * @notice Transfers the fee to the fee recipient
   * @param token The token to transfer
   * @param fee The fee percentage in basis points
   * @param amount The amount of tokens to transfer
   * @return amount The amount of tokens after the fee is transferred
   */
  function _transferFee(address token, uint16 fee, uint256 amount) internal returns (uint256, uint256 feeAmount) {
    if (fee > 0) {
      feeAmount = (amount * fee) / MAX_FEE_BPS;
      _returnAssetWithAmount(token, feeRecipient, feeAmount);
      amount -= feeAmount;
    }
    return (amount, feeAmount);
  }

  /**
   * @notice Converts input token balance of address(this) to vault's desired token
   * @param asset The asset to convert
   * @param tokenIn The input token to convert
   * @return assetsOut Amount of converted assets
   * @return returnedAssets Array of any remaining tokens returned to caller
   */
  function swapToAssets(
    address asset,
    address tokenIn,
    uint256 tokenInAmount,
    address recipient
  ) public virtual returns (uint256 assetsOut, ReturnedAsset[] memory returnedAssets);

  /**
   * @notice Converts vault's desired token balance to output token
   * @param asset The asset to convert
   * @param tokenOut The output token to convert
   * @return tokenOutAmount Amount of converted tokens
   * @return returnedAssets Array of any remaining tokens returned to caller
   */
  function swapFromAssets(
    address asset,
    address tokenOut,
    uint256 assetsInAmount,
    address recipient
  ) public virtual returns (uint256 tokenOutAmount, ReturnedAsset[] memory returnedAssets);

  /**
   * @notice Deposits tokens into a vault after converting them if necessary
   * @param vault Target vault
   * @param tokenIn Input token address
   * @param tokenInAmount Amount of input tokens
   * @param minShares Minimum amount of vault shares to receive
   * @return shares Number of vault shares received
   * @return returnedAssets Array of any remaining tokens returned to caller
   */
  function zapIn(
    IVault vault,
    address tokenIn,
    uint256 tokenInAmount,
    uint256 minShares
  ) external payable virtual nonReentrant returns (uint256 shares, ReturnedAsset[] memory returnedAssets) {
    // if eth is the tokenIn, we need to convert it to the wrappedNative
    if (tokenIn == address(0)) {
      if (msg.value < MINIMUM_AMOUNT) revert InsufficientInputAmount(address(0), msg.value, MINIMUM_AMOUNT);
      wrappedNative.deposit{value: msg.value}();
      tokenIn = address(wrappedNative);
      tokenInAmount = msg.value;
    } else {
      if (tokenInAmount < MINIMUM_AMOUNT) revert InsufficientInputAmount(tokenIn, tokenInAmount, MINIMUM_AMOUNT);
      tokenInAmount = _safeTransferFromTokens(tokenIn, tokenInAmount);
    }

    // transfer the fee
    uint256 feeAmount;
    (tokenInAmount, feeAmount) = _transferFee(tokenIn, zapInFee, tokenInAmount);

    // convert the input token to the vault's asset if needed
    uint256 assetsIn = tokenInAmount;
    if (vault.asset() != tokenIn) {
      (assetsIn, returnedAssets) = swapToAssets(vault.asset(), tokenIn, tokenInAmount, address(this));
    }

    // approve the asset to the vault
    IERC20(vault.asset()).forceApprove(address(vault), assetsIn);

    // deposit the asset to the vault
    shares = vault.deposit(assetsIn, msg.sender, minShares);
    emit ZapIn(msg.sender, address(vault), tokenIn, tokenInAmount, assetsIn, shares, feeAmount, returnedAssets);
  }

  /**
   * @notice Withdraws from vault and converts to desired token
   * @param vault Source vault
   * @param sharesAmount Amount of vault shares to withdraw
   * @param tokenOut Desired output token
   * @param minTokenOutAmount Minimum amount of desired tokens to receive
   * @return tokenOutAmount Amount of output tokens received
   * @return returnedAssets Array of any remaining tokens returned to caller
   */
  function zapOut(
    IVault vault,
    uint256 sharesAmount,
    address tokenOut,
    uint256 minTokenOutAmount
  ) external virtual nonReentrant returns (uint256 tokenOutAmount, ReturnedAsset[] memory returnedAssets) {
    uint256 assetsOut = IVault(vault).redeem(sharesAmount, address(this), msg.sender);

    // if eth is the desiredToken, we need to convert it to the wrappedNative
    if (tokenOut == address(0)) {
      tokenOut = address(wrappedNative);
    }

    // convert the asset to the desired token if needed
    if (vault.asset() == tokenOut) {
      tokenOutAmount = assetsOut;
    } else {
      (tokenOutAmount, returnedAssets) = swapFromAssets(vault.asset(), tokenOut, assetsOut, address(this));
    }

    // transfer the fee
    uint256 feeAmount;
    (tokenOutAmount, feeAmount) = _transferFee(tokenOut, zapOutFee, tokenOutAmount);

    // return the token
    _returnAsset(tokenOut, msg.sender);

    if (tokenOutAmount < minTokenOutAmount)
      revert InsufficientOutputAmount(tokenOut, tokenOutAmount, minTokenOutAmount);
    emit ZapOut(
      msg.sender,
      address(vault),
      tokenOut,
      tokenOutAmount,
      assetsOut,
      sharesAmount,
      feeAmount,
      returnedAssets
    );
  }
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 100
  },
  "evmVersion": "paris",
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  }
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"governanceAddress","type":"address"},{"internalType":"address","name":"wrappedNative","type":"address"},{"internalType":"address","name":"stableCoin","type":"address"},{"internalType":"address","name":"swapRouter","type":"address"},{"internalType":"address","name":"lpRouter","type":"address"},{"internalType":"address","name":"feeRecipient","type":"address"},{"internalType":"uint16","name":"zapInFee","type":"uint16"},{"internalType":"uint16","name":"zapOutFee","type":"uint16"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[],"name":"ETHTransferFailed","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[{"internalType":"uint16","name":"fee","type":"uint16"},{"internalType":"uint16","name":"maxFee","type":"uint16"}],"name":"FeeTooHigh","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"provided","type":"uint256"},{"internalType":"uint256","name":"minimum","type":"uint256"}],"name":"InsufficientInputAmount","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"received","type":"uint256"},{"internalType":"uint256","name":"minimum","type":"uint256"}],"name":"InsufficientOutputAmount","type":"error"},{"inputs":[],"name":"NotGovernance","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[],"name":"TokenNotApproved","type":"error"},{"inputs":[],"name":"TotalRatioZero","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldRecipient","type":"address"},{"indexed":true,"internalType":"address","name":"newRecipient","type":"address"}],"name":"FeeRecipientChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newGovernance","type":"address"},{"indexed":true,"internalType":"address","name":"oldGovernance","type":"address"}],"name":"GovernanceChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newLpRouter","type":"address"},{"indexed":true,"internalType":"address","name":"oldLpRouter","type":"address"}],"name":"LpRouterChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newStableCoin","type":"address"},{"indexed":true,"internalType":"address","name":"oldStableCoin","type":"address"}],"name":"StableCoinChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newSwapRouter","type":"address"},{"indexed":true,"internalType":"address","name":"oldSwapRouter","type":"address"}],"name":"SwapRouterChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"vault","type":"address"},{"indexed":false,"internalType":"address","name":"tokenIn","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenInAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"assetsIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"},{"components":[{"internalType":"address","name":"tokens","type":"address"},{"internalType":"uint256","name":"amounts","type":"uint256"}],"indexed":false,"internalType":"struct IZapper.ReturnedAsset[]","name":"returnedAssets","type":"tuple[]"}],"name":"ZapIn","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint16","name":"oldFee","type":"uint16"},{"indexed":false,"internalType":"uint16","name":"newFee","type":"uint16"}],"name":"ZapInFeeChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"vault","type":"address"},{"indexed":false,"internalType":"address","name":"tokenOut","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenOutAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"assetsOut","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"},{"components":[{"internalType":"address","name":"tokens","type":"address"},{"internalType":"uint256","name":"amounts","type":"uint256"}],"indexed":false,"internalType":"struct IZapper.ReturnedAsset[]","name":"returnedAssets","type":"tuple[]"}],"name":"ZapOut","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint16","name":"oldFee","type":"uint16"},{"indexed":false,"internalType":"uint16","name":"newFee","type":"uint16"}],"name":"ZapOutFeeChanged","type":"event"},{"inputs":[],"name":"MAX_FEE","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_FEE_BPS","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MINIMUM_AMOUNT","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feeRecipient","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"governance","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lpRouter","outputs":[{"internalType":"contract ILpRouter","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newRecipient","type":"address"}],"name":"setFeeRecipient","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"governanceAddress","type":"address"}],"name":"setGovernance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"routerAddress","type":"address"}],"name":"setLpRouter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"stablecoinAddress","type":"address"}],"name":"setStableCoin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"routerAddress","type":"address"}],"name":"setSwapRouter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"newFee","type":"uint16"}],"name":"setZapInFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"newFee","type":"uint16"}],"name":"setZapOutFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stablecoin","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"assetsInAmount","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"name":"swapFromAssets","outputs":[{"internalType":"uint256","name":"tokenOutAmount","type":"uint256"},{"components":[{"internalType":"address","name":"tokens","type":"address"},{"internalType":"uint256","name":"amounts","type":"uint256"}],"internalType":"struct IZapper.ReturnedAsset[]","name":"returnedAssets","type":"tuple[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"swapRouter","outputs":[{"internalType":"contract ISwapRouter","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"uint256","name":"tokenInAmount","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"name":"swapToAssets","outputs":[{"internalType":"uint256","name":"tokenOutAmount","type":"uint256"},{"components":[{"internalType":"address","name":"tokens","type":"address"},{"internalType":"uint256","name":"amounts","type":"uint256"}],"internalType":"struct IZapper.ReturnedAsset[]","name":"returnedAssets","type":"tuple[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"wrappedNative","outputs":[{"internalType":"contract IWETH","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IVault","name":"vault","type":"address"},{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"uint256","name":"tokenInAmount","type":"uint256"},{"internalType":"uint256","name":"minShares","type":"uint256"}],"name":"zapIn","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"},{"components":[{"internalType":"address","name":"tokens","type":"address"},{"internalType":"uint256","name":"amounts","type":"uint256"}],"internalType":"struct IZapper.ReturnedAsset[]","name":"returnedAssets","type":"tuple[]"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"zapInFee","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IVault","name":"vault","type":"address"},{"internalType":"uint256","name":"sharesAmount","type":"uint256"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"minTokenOutAmount","type":"uint256"}],"name":"zapOut","outputs":[{"internalType":"uint256","name":"tokenOutAmount","type":"uint256"},{"components":[{"internalType":"address","name":"tokens","type":"address"},{"internalType":"uint256","name":"amounts","type":"uint256"}],"internalType":"struct IZapper.ReturnedAsset[]","name":"returnedAssets","type":"tuple[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"zapOutFee","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]

60a06040523480156200001157600080fd5b50604051620022c9380380620022c98339810160408190526200003491620002f4565b600160005587878787878787876001600160a01b03881615806200005f57506001600160a01b038716155b806200007257506001600160a01b038616155b806200008557506001600160a01b038516155b806200009857506001600160a01b038416155b80620000ab57506001600160a01b038316155b15620000ca5760405163d92e233d60e01b815260040160405180910390fd5b600280546001600160a01b0319166001600160a01b038a8116919091179091558716608081905260408051630d0e30db60e41b8152905163d0e30db0916000916004808301928492919082900301818388803b1580156200012a57600080fd5b505af11580156200013f573d6000803e3d6000fd5b5050608051604051632e1a7d4d60e01b8152600060048201526001600160a01b039091169350632e1a7d4d92506024019050600060405180830381600087803b1580156200018c57600080fd5b505af1158015620001a1573d6000803e3d6000fd5b5050600180546001600160a01b03808b1664010000000002600160201b600160c01b031990921691909117909155600380548983166001600160a01b031991821617909155600480549289169290911691909117905550506103e861ffff831611156200023457604051633c057d7b60e01b815261ffff831660048201526103e860248201526044015b60405180910390fd5b6103e861ffff821611156200026b57604051633c057d7b60e01b815261ffff821660048201526103e860248201526044016200022b565b600580546001600160a01b039094166001600160a01b0319909416939093179092556001805461ffff938416620100000263ffffffff199091169390921692909217179055506200039c9b505050505050505050505050565b80516001600160a01b0381168114620002dc57600080fd5b919050565b805161ffff81168114620002dc57600080fd5b600080600080600080600080610100898b0312156200031257600080fd5b6200031d89620002c4565b97506200032d60208a01620002c4565b96506200033d60408a01620002c4565b95506200034d60608a01620002c4565b94506200035d60808a01620002c4565b93506200036d60a08a01620002c4565b92506200037d60c08a01620002e1565b91506200038d60e08a01620002e1565b90509295985092959890939650565b608051611ed9620003f0600039600081816101490152818161040901528181610685015281816106f901528181610b9a015281816115c60152818161162c0152818161177c01526117c90152611ed96000f3fe6080604052600436106101395760003560e01c8063a863ff2f116100ab578063c31c9c071161006f578063c31c9c0714610379578063d55be8c614610399578063e74b981b146103af578063e9cbd822146103cf578063eb6d3a11146103f7578063f99e63871461042b57600080fd5b8063a863ff2f146102fe578063a89635bc1461031e578063ab033ea914610339578063afb44e3714610359578063bc063e1a146101f357600080fd5b80633c3b1ef6116100fd5780633c3b1ef61461024a578063412736571461025d578063469048401461027d5780635aa6e6751461029d5780638f0af424146102bd5780639739c03e146102dd57600080fd5b80630c8106dc1461017d57806315a29203146101b357806323af4e17146101d3578063257d9bb8146101f35780633218e83a1461021c57600080fd5b3661017857336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461017657610176611a9f565b005b600080fd5b34801561018957600080fd5b5060045461019d906001600160a01b031681565b6040516101aa9190611ab5565b60405180910390f35b3480156101bf57600080fd5b506101766101ce366004611ade565b61044b565b3480156101df57600080fd5b506101766101ee366004611ade565b6104d1565b3480156101ff57600080fd5b506102096103e881565b60405161ffff90911681526020016101aa565b34801561022857600080fd5b5061023c610237366004611afb565b610567565b6040516101aa929190611b9d565b61023c610258366004611bb6565b610634565b34801561026957600080fd5b50610176610278366004611ade565b6109c4565b34801561028957600080fd5b5060055461019d906001600160a01b031681565b3480156102a957600080fd5b5060025461019d906001600160a01b031681565b3480156102c957600080fd5b506101766102d8366004611bfc565b610a4a565b3480156102e957600080fd5b506001546102099062010000900461ffff1681565b34801561030a57600080fd5b5061023c610319366004611c20565b610b02565b34801561032a57600080fd5b506001546102099061ffff1681565b34801561034557600080fd5b50610176610354366004611ade565b610d65565b34801561036557600080fd5b50610176610374366004611bfc565b610de5565b34801561038557600080fd5b5060035461019d906001600160a01b031681565b3480156103a557600080fd5b5061020961271081565b3480156103bb57600080fd5b506101766103ca366004611ade565b610ea1565b3480156103db57600080fd5b5060015461019d9064010000000090046001600160a01b031681565b34801561040357600080fd5b5061019d7f000000000000000000000000000000000000000000000000000000000000000081565b34801561043757600080fd5b5061023c610446366004611afb565b610f27565b6002546001600160a01b0316331461047657604051632d5be4cb60e21b815260040160405180910390fd5b61047f81610ff9565b600480546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f82c52d8aa62200dfc997bdf1100b1c2ed614580941b2b8637d10b8ca0428eeec90600090a35050565b6002546001600160a01b031633146104fc57604051632d5be4cb60e21b815260040160405180910390fd5b61050581610ff9565b60018054640100000000600160c01b0319166401000000006001600160a01b03848116828102939093179384905560405192939190910416907f497b8419a37c8014b1a755ec707f25d11ae4bade591d379f9a4095f37edaf76e90600090a350565b600060606001600160a01b0383163014610588576105858585611023565b93505b6004546105a2906001600160a01b038781169116866111c3565b6004805460405163190a4d4960e01b81526001600160a01b039091169163190a4d49916105da918a918a918a918a9160069101611c8a565b6020604051808303816000875af11580156105f9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061061d9190611cbe565b91506106298533611220565b905094509492505050565b6000606061064061128c565b6001600160a01b038516610722576103e8341015610683576000346103e8604051630b722aef60e21b815260040161067a93929190611cd7565b60405180910390fd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d0e30db0346040518263ffffffff1660e01b81526004016000604051808303818588803b1580156106de57600080fd5b505af11580156106f2573d6000803e3d6000fd5b50505050507f0000000000000000000000000000000000000000000000000000000000000000945034935061075a565b6103e884101561074d5784846103e8604051630b722aef60e21b815260040161067a93929190611cd7565b6107578585611023565b93505b60015460009061077090879061ffff16876112b6565b80925081965050506000859050866001600160a01b0316886001600160a01b03166338d52e0f6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156107c5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107e99190611cfc565b6001600160a01b03161461086957610864886001600160a01b03166338d52e0f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610838573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061085c9190611cfc565b888830610567565b935090505b6108e088828a6001600160a01b03166338d52e0f6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156108ac573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108d09190611cfc565b6001600160a01b03169190611311565b60405163bc157ac160e01b815260048101829052336024820152604481018690526001600160a01b0389169063bc157ac1906064016020604051808303816000875af1158015610934573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109589190611cbe565b9350876001600160a01b0316336001600160a01b03167f87640c2e40d67703e350e7d24d4e416e5c4f1e1550e1285b1bf667440d36bdcf89898589888a6040516109a796959493929190611d19565b60405180910390a350506109bb6001600055565b94509492505050565b6002546001600160a01b031633146109ef57604051632d5be4cb60e21b815260040160405180910390fd5b6109f881610ff9565b600380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f5c5b0bb5085481c320dc523e3b3f829eaa807936f615add0077fa213f345090e90600090a35050565b6002546001600160a01b03163314610a7557604051632d5be4cb60e21b815260040160405180910390fd5b6103e861ffff82161115610aaa57604051633c057d7b60e01b815261ffff821660048201526103e8602482015260440161067a565b6001805461ffff83811661ffff1983168117909355604080519190921680825260208201939093527fcd5c26e400cb366bdac1d1cbdec4b428f15bc14a133ecbda85bc6f11f7acb9f191015b60405180910390a15050565b60006060610b0e61128c565b604051635d043b2960e11b8152600481018690523060248201523360448201526000906001600160a01b0388169063ba087652906064016020604051808303816000875af1158015610b64573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b889190611cbe565b90506001600160a01b038516610bbc577f000000000000000000000000000000000000000000000000000000000000000094505b846001600160a01b0316876001600160a01b03166338d52e0f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610c04573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c289190611cfc565b6001600160a01b031603610c3e57809250610cb1565b610cab876001600160a01b03166338d52e0f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610c7f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ca39190611cfc565b868330610f27565b90935091505b6000610cce86600160029054906101000a900461ffff16866112b6565b9094509050610cdd8633611220565b5084841015610d1857604051632752fb4960e11b81526001600160a01b0387166004820152602481018590526044810186905260640161067a565b876001600160a01b0316336001600160a01b03167f0bc6c5ccdba0b566b3adbb7c24c419a1b872218cd487d145a4e4eb70b2c8bfb38887868c878a6040516109a796959493929190611d19565b6002546001600160a01b03163314610d9057604051632d5be4cb60e21b815260040160405180910390fd5b610d9981610ff9565b600280546001600160a01b0319166001600160a01b03831690811790915560405181907f3aaaebeb4821d6a7e5c77ece53cff0afcc56c82add2c978dbbb7f73e84cbcfd290600090a350565b6002546001600160a01b03163314610e1057604051632d5be4cb60e21b815260040160405180910390fd5b6103e861ffff82161115610e4557604051633c057d7b60e01b815261ffff821660048201526103e8602482015260440161067a565b6001805461ffff8381166201000081810263ffff00001985161790945560408051949093049091168084526020840191909152917febcac71ae552a15ae4d23c933603e1a71f47ff8371bdd98194f037156b73a7199101610af6565b6002546001600160a01b03163314610ecc57604051632d5be4cb60e21b815260040160405180910390fd5b610ed581610ff9565b600580546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f0bc21fe5c3ab742ff1d15b5c4477ffbacf1167e618228078fa625edebe7f331d90600090a35050565b600060606001600160a01b0383163014610f4857610f458685611023565b93505b600454610f62906001600160a01b038881169116866111c3565b6004805460405163616a3dbd60e11b81526001600160a01b039091169163c2d47b7a91610f9a918a91899130918c9160069101611d5e565b6020604051808303816000875af1158015610fb9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fdd9190611cbe565b91506001600160a01b03831630146109bb576106298584611220565b6001600160a01b0381166110205760405163d92e233d60e01b815260040160405180910390fd5b50565b604051636eb1769f60e11b815233600482015230602482015260009082906001600160a01b0385169063dd62ed3e90604401602060405180830381865afa158015611072573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110969190611cbe565b10156110b5576040516332da96a360e01b815260040160405180910390fd5b6040516370a0823160e01b81526000906001600160a01b038516906370a08231906110e4903090600401611ab5565b602060405180830381865afa158015611101573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111259190611cbe565b905061113c6001600160a01b0385163330866113a9565b6040516370a0823160e01b81526000906001600160a01b038616906370a082319061116b903090600401611ab5565b602060405180830381865afa158015611188573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111ac9190611cbe565b90506111b88282611da8565b925050505b92915050565b61121b83846001600160a01b031663a9059cbb85856040516024016111e9929190611dbb565b604051602081830303815290604052915060e01b6020820180516001600160e01b0383818316178352505050506113e2565b505050565b6040805160018082528183019092526060916000919060208083019080368337019050509050838160008151811061125a5761125a611dea565b60200260200101906001600160a01b031690816001600160a01b031681525050611284818461143c565b949350505050565b6002600054036112af57604051633ee5aeb560e01b815260040160405180910390fd5b6002600055565b60008061ffff841615611306576127106112d461ffff861685611e00565b6112de9190611e17565b6005549091506112f99086906001600160a01b031683611774565b6113038184611da8565b92505b829150935093915050565b6000836001600160a01b031663095ea7b38484604051602401611335929190611dbb565b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050905061136e84826118ce565b6113a35761139984856001600160a01b031663095ea7b38660006040516024016111e9929190611dbb565b6113a384826113e2565b50505050565b6040516001600160a01b0384811660248301528381166044830152606482018390526113a39186918216906323b872dd906084016111e9565b60006113f76001600160a01b03841683611971565b9050805160001415801561141c57508080602001905181019061141a9190611e39565b155b1561121b5782604051635274afe760e01b815260040161067a9190611ab5565b60606000835167ffffffffffffffff81111561145a5761145a611dd4565b60405190808252806020026020018201604052801561149f57816020015b60408051808201909152600080825260208201528152602001906001900390816114785790505b50915060005b845181101561176c5760006001600160a01b03168582815181106114cb576114cb611dea565b60200260200101516001600160a01b0316031561175a578481815181106114f4576114f4611dea565b60200260200101516001600160a01b03166370a08231306040518263ffffffff1660e01b81526004016115279190611ab5565b602060405180830381865afa158015611544573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115689190611cbe565b9150604051806040016040528086838151811061158757611587611dea565b60200260200101516001600160a01b03168152602001838152508382815181106115b3576115b3611dea565b6020908102919091010152811561175a577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031685828151811061160057611600611dea565b60200260200101516001600160a01b03160361172357604051632e1a7d4d60e01b8152600481018390527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690632e1a7d4d90602401600060405180830381600087803b15801561167857600080fd5b505af115801561168c573d6000803e3d6000fd5b5050604080516000808252602082019283905293506001600160a01b038816925085916116b99190611e5b565b60006040518083038185875af1925050503d80600081146116f6576040519150601f19603f3d011682016040523d82523d6000602084013e6116fb565b606091505b505090508061171d5760405163b12d13eb60e01b815260040160405180910390fd5b5061175a565b61175a848387848151811061173a5761173a611dea565b60200260200101516001600160a01b03166111c39092919063ffffffff16565b8061176481611e8a565b9150506114a5565b505092915050565b801561121b577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316836001600160a01b0316036118ba57604051632e1a7d4d60e01b8152600481018290527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690632e1a7d4d90602401600060405180830381600087803b15801561181557600080fd5b505af1158015611829573d6000803e3d6000fd5b5050604080516000808252602082019283905293506001600160a01b038616925084916118569190611e5b565b60006040518083038185875af1925050503d8060008114611893576040519150601f19603f3d011682016040523d82523d6000602084013e611898565b606091505b50509050806113a35760405163b12d13eb60e01b815260040160405180910390fd5b61121b6001600160a01b03841683836111c3565b6000806000846001600160a01b0316846040516118eb9190611e5b565b6000604051808303816000865af19150503d8060008114611928576040519150601f19603f3d011682016040523d82523d6000602084013e61192d565b606091505b50915091508180156119575750805115806119575750808060200190518101906119579190611e39565b80156111b85750505050506001600160a01b03163b151590565b606061197f83836000611986565b9392505050565b6060814710156119ab573060405163cd78605960e01b815260040161067a9190611ab5565b600080856001600160a01b031684866040516119c79190611e5b565b60006040518083038185875af1925050503d8060008114611a04576040519150601f19603f3d011682016040523d82523d6000602084013e611a09565b606091505b5091509150611a19868383611a23565b9695505050505050565b606082611a3857611a3382611a76565b61197f565b8151158015611a4f57506001600160a01b0384163b155b15611a6f5783604051639996b31560e01b815260040161067a9190611ab5565b508061197f565b805115611a865780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b634e487b7160e01b600052600160045260246000fd5b6001600160a01b0391909116815260200190565b6001600160a01b038116811461102057600080fd5b600060208284031215611af057600080fd5b813561197f81611ac9565b60008060008060808587031215611b1157600080fd5b8435611b1c81611ac9565b93506020850135611b2c81611ac9565b9250604085013591506060850135611b4381611ac9565b939692955090935050565b600081518084526020808501945080840160005b83811015611b9257815180516001600160a01b031688528301518388015260409096019590820190600101611b62565b509495945050505050565b8281526040602082015260006112846040830184611b4e565b60008060008060808587031215611bcc57600080fd5b8435611bd781611ac9565b93506020850135611be781611ac9565b93969395505050506040820135916060013590565b600060208284031215611c0e57600080fd5b813561ffff8116811461197f57600080fd5b60008060008060808587031215611c3657600080fd5b8435611c4181611ac9565b9350602085013592506040850135611c5881611ac9565b9396929550929360600135925050565b600a8110611c8657634e487b7160e01b600052602160045260246000fd5b9052565b6001600160a01b0386811682528581166020830152604082018590528316606082015260a08101611a196080830184611c68565b600060208284031215611cd057600080fd5b5051919050565b6001600160a01b03939093168352602083019190915261ffff16604082015260600190565b600060208284031215611d0e57600080fd5b815161197f81611ac9565b60018060a01b038716815285602082015284604082015283606082015282608082015260c060a08201526000611d5260c0830184611b4e565b98975050505050505050565b6001600160a01b0386811682526020820186905284811660408301528316606082015260a08101611a196080830184611c68565b634e487b7160e01b600052601160045260246000fd5b818103818111156111bd576111bd611d92565b6001600160a01b03929092168252602082015260400190565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b80820281158282048414176111bd576111bd611d92565b600082611e3457634e487b7160e01b600052601260045260246000fd5b500490565b600060208284031215611e4b57600080fd5b8151801515811461197f57600080fd5b6000825160005b81811015611e7c5760208186018101518583015201611e62565b506000920191825250919050565b600060018201611e9c57611e9c611d92565b506001019056fea2646970667358221220a3aa4be3533f21a6e26311cd954b0f508c1871c08deacb9e90b2a2207ad83c0e64736f6c6343000814003300000000000000000000000062137cec7754aa3f530c98866a86dba36d2687170000000000000000000000006969696969696969696969696969696969696969000000000000000000000000fcbd14dc51f0a4d49d5e53c2e0950e0bc26d0dce000000000000000000000000b8ba8721a5bfcf828a46c5f839c559988d8d5ead000000000000000000000000609e7b64a2970157789ae6e396de1b0b8e393b050000000000000000000000001cfe31bfa1ac9b28588c91bb4300a1ed032f069f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

Deployed Bytecode

0x6080604052600436106101395760003560e01c8063a863ff2f116100ab578063c31c9c071161006f578063c31c9c0714610379578063d55be8c614610399578063e74b981b146103af578063e9cbd822146103cf578063eb6d3a11146103f7578063f99e63871461042b57600080fd5b8063a863ff2f146102fe578063a89635bc1461031e578063ab033ea914610339578063afb44e3714610359578063bc063e1a146101f357600080fd5b80633c3b1ef6116100fd5780633c3b1ef61461024a578063412736571461025d578063469048401461027d5780635aa6e6751461029d5780638f0af424146102bd5780639739c03e146102dd57600080fd5b80630c8106dc1461017d57806315a29203146101b357806323af4e17146101d3578063257d9bb8146101f35780633218e83a1461021c57600080fd5b3661017857336001600160a01b037f0000000000000000000000006969696969696969696969696969696969696969161461017657610176611a9f565b005b600080fd5b34801561018957600080fd5b5060045461019d906001600160a01b031681565b6040516101aa9190611ab5565b60405180910390f35b3480156101bf57600080fd5b506101766101ce366004611ade565b61044b565b3480156101df57600080fd5b506101766101ee366004611ade565b6104d1565b3480156101ff57600080fd5b506102096103e881565b60405161ffff90911681526020016101aa565b34801561022857600080fd5b5061023c610237366004611afb565b610567565b6040516101aa929190611b9d565b61023c610258366004611bb6565b610634565b34801561026957600080fd5b50610176610278366004611ade565b6109c4565b34801561028957600080fd5b5060055461019d906001600160a01b031681565b3480156102a957600080fd5b5060025461019d906001600160a01b031681565b3480156102c957600080fd5b506101766102d8366004611bfc565b610a4a565b3480156102e957600080fd5b506001546102099062010000900461ffff1681565b34801561030a57600080fd5b5061023c610319366004611c20565b610b02565b34801561032a57600080fd5b506001546102099061ffff1681565b34801561034557600080fd5b50610176610354366004611ade565b610d65565b34801561036557600080fd5b50610176610374366004611bfc565b610de5565b34801561038557600080fd5b5060035461019d906001600160a01b031681565b3480156103a557600080fd5b5061020961271081565b3480156103bb57600080fd5b506101766103ca366004611ade565b610ea1565b3480156103db57600080fd5b5060015461019d9064010000000090046001600160a01b031681565b34801561040357600080fd5b5061019d7f000000000000000000000000696969696969696969696969696969696969696981565b34801561043757600080fd5b5061023c610446366004611afb565b610f27565b6002546001600160a01b0316331461047657604051632d5be4cb60e21b815260040160405180910390fd5b61047f81610ff9565b600480546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f82c52d8aa62200dfc997bdf1100b1c2ed614580941b2b8637d10b8ca0428eeec90600090a35050565b6002546001600160a01b031633146104fc57604051632d5be4cb60e21b815260040160405180910390fd5b61050581610ff9565b60018054640100000000600160c01b0319166401000000006001600160a01b03848116828102939093179384905560405192939190910416907f497b8419a37c8014b1a755ec707f25d11ae4bade591d379f9a4095f37edaf76e90600090a350565b600060606001600160a01b0383163014610588576105858585611023565b93505b6004546105a2906001600160a01b038781169116866111c3565b6004805460405163190a4d4960e01b81526001600160a01b039091169163190a4d49916105da918a918a918a918a9160069101611c8a565b6020604051808303816000875af11580156105f9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061061d9190611cbe565b91506106298533611220565b905094509492505050565b6000606061064061128c565b6001600160a01b038516610722576103e8341015610683576000346103e8604051630b722aef60e21b815260040161067a93929190611cd7565b60405180910390fd5b7f00000000000000000000000069696969696969696969696969696969696969696001600160a01b031663d0e30db0346040518263ffffffff1660e01b81526004016000604051808303818588803b1580156106de57600080fd5b505af11580156106f2573d6000803e3d6000fd5b50505050507f0000000000000000000000006969696969696969696969696969696969696969945034935061075a565b6103e884101561074d5784846103e8604051630b722aef60e21b815260040161067a93929190611cd7565b6107578585611023565b93505b60015460009061077090879061ffff16876112b6565b80925081965050506000859050866001600160a01b0316886001600160a01b03166338d52e0f6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156107c5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107e99190611cfc565b6001600160a01b03161461086957610864886001600160a01b03166338d52e0f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610838573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061085c9190611cfc565b888830610567565b935090505b6108e088828a6001600160a01b03166338d52e0f6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156108ac573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108d09190611cfc565b6001600160a01b03169190611311565b60405163bc157ac160e01b815260048101829052336024820152604481018690526001600160a01b0389169063bc157ac1906064016020604051808303816000875af1158015610934573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109589190611cbe565b9350876001600160a01b0316336001600160a01b03167f87640c2e40d67703e350e7d24d4e416e5c4f1e1550e1285b1bf667440d36bdcf89898589888a6040516109a796959493929190611d19565b60405180910390a350506109bb6001600055565b94509492505050565b6002546001600160a01b031633146109ef57604051632d5be4cb60e21b815260040160405180910390fd5b6109f881610ff9565b600380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f5c5b0bb5085481c320dc523e3b3f829eaa807936f615add0077fa213f345090e90600090a35050565b6002546001600160a01b03163314610a7557604051632d5be4cb60e21b815260040160405180910390fd5b6103e861ffff82161115610aaa57604051633c057d7b60e01b815261ffff821660048201526103e8602482015260440161067a565b6001805461ffff83811661ffff1983168117909355604080519190921680825260208201939093527fcd5c26e400cb366bdac1d1cbdec4b428f15bc14a133ecbda85bc6f11f7acb9f191015b60405180910390a15050565b60006060610b0e61128c565b604051635d043b2960e11b8152600481018690523060248201523360448201526000906001600160a01b0388169063ba087652906064016020604051808303816000875af1158015610b64573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b889190611cbe565b90506001600160a01b038516610bbc577f000000000000000000000000696969696969696969696969696969696969696994505b846001600160a01b0316876001600160a01b03166338d52e0f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610c04573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c289190611cfc565b6001600160a01b031603610c3e57809250610cb1565b610cab876001600160a01b03166338d52e0f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610c7f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ca39190611cfc565b868330610f27565b90935091505b6000610cce86600160029054906101000a900461ffff16866112b6565b9094509050610cdd8633611220565b5084841015610d1857604051632752fb4960e11b81526001600160a01b0387166004820152602481018590526044810186905260640161067a565b876001600160a01b0316336001600160a01b03167f0bc6c5ccdba0b566b3adbb7c24c419a1b872218cd487d145a4e4eb70b2c8bfb38887868c878a6040516109a796959493929190611d19565b6002546001600160a01b03163314610d9057604051632d5be4cb60e21b815260040160405180910390fd5b610d9981610ff9565b600280546001600160a01b0319166001600160a01b03831690811790915560405181907f3aaaebeb4821d6a7e5c77ece53cff0afcc56c82add2c978dbbb7f73e84cbcfd290600090a350565b6002546001600160a01b03163314610e1057604051632d5be4cb60e21b815260040160405180910390fd5b6103e861ffff82161115610e4557604051633c057d7b60e01b815261ffff821660048201526103e8602482015260440161067a565b6001805461ffff8381166201000081810263ffff00001985161790945560408051949093049091168084526020840191909152917febcac71ae552a15ae4d23c933603e1a71f47ff8371bdd98194f037156b73a7199101610af6565b6002546001600160a01b03163314610ecc57604051632d5be4cb60e21b815260040160405180910390fd5b610ed581610ff9565b600580546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f0bc21fe5c3ab742ff1d15b5c4477ffbacf1167e618228078fa625edebe7f331d90600090a35050565b600060606001600160a01b0383163014610f4857610f458685611023565b93505b600454610f62906001600160a01b038881169116866111c3565b6004805460405163616a3dbd60e11b81526001600160a01b039091169163c2d47b7a91610f9a918a91899130918c9160069101611d5e565b6020604051808303816000875af1158015610fb9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fdd9190611cbe565b91506001600160a01b03831630146109bb576106298584611220565b6001600160a01b0381166110205760405163d92e233d60e01b815260040160405180910390fd5b50565b604051636eb1769f60e11b815233600482015230602482015260009082906001600160a01b0385169063dd62ed3e90604401602060405180830381865afa158015611072573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110969190611cbe565b10156110b5576040516332da96a360e01b815260040160405180910390fd5b6040516370a0823160e01b81526000906001600160a01b038516906370a08231906110e4903090600401611ab5565b602060405180830381865afa158015611101573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111259190611cbe565b905061113c6001600160a01b0385163330866113a9565b6040516370a0823160e01b81526000906001600160a01b038616906370a082319061116b903090600401611ab5565b602060405180830381865afa158015611188573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111ac9190611cbe565b90506111b88282611da8565b925050505b92915050565b61121b83846001600160a01b031663a9059cbb85856040516024016111e9929190611dbb565b604051602081830303815290604052915060e01b6020820180516001600160e01b0383818316178352505050506113e2565b505050565b6040805160018082528183019092526060916000919060208083019080368337019050509050838160008151811061125a5761125a611dea565b60200260200101906001600160a01b031690816001600160a01b031681525050611284818461143c565b949350505050565b6002600054036112af57604051633ee5aeb560e01b815260040160405180910390fd5b6002600055565b60008061ffff841615611306576127106112d461ffff861685611e00565b6112de9190611e17565b6005549091506112f99086906001600160a01b031683611774565b6113038184611da8565b92505b829150935093915050565b6000836001600160a01b031663095ea7b38484604051602401611335929190611dbb565b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050905061136e84826118ce565b6113a35761139984856001600160a01b031663095ea7b38660006040516024016111e9929190611dbb565b6113a384826113e2565b50505050565b6040516001600160a01b0384811660248301528381166044830152606482018390526113a39186918216906323b872dd906084016111e9565b60006113f76001600160a01b03841683611971565b9050805160001415801561141c57508080602001905181019061141a9190611e39565b155b1561121b5782604051635274afe760e01b815260040161067a9190611ab5565b60606000835167ffffffffffffffff81111561145a5761145a611dd4565b60405190808252806020026020018201604052801561149f57816020015b60408051808201909152600080825260208201528152602001906001900390816114785790505b50915060005b845181101561176c5760006001600160a01b03168582815181106114cb576114cb611dea565b60200260200101516001600160a01b0316031561175a578481815181106114f4576114f4611dea565b60200260200101516001600160a01b03166370a08231306040518263ffffffff1660e01b81526004016115279190611ab5565b602060405180830381865afa158015611544573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115689190611cbe565b9150604051806040016040528086838151811061158757611587611dea565b60200260200101516001600160a01b03168152602001838152508382815181106115b3576115b3611dea565b6020908102919091010152811561175a577f00000000000000000000000069696969696969696969696969696969696969696001600160a01b031685828151811061160057611600611dea565b60200260200101516001600160a01b03160361172357604051632e1a7d4d60e01b8152600481018390527f00000000000000000000000069696969696969696969696969696969696969696001600160a01b031690632e1a7d4d90602401600060405180830381600087803b15801561167857600080fd5b505af115801561168c573d6000803e3d6000fd5b5050604080516000808252602082019283905293506001600160a01b038816925085916116b99190611e5b565b60006040518083038185875af1925050503d80600081146116f6576040519150601f19603f3d011682016040523d82523d6000602084013e6116fb565b606091505b505090508061171d5760405163b12d13eb60e01b815260040160405180910390fd5b5061175a565b61175a848387848151811061173a5761173a611dea565b60200260200101516001600160a01b03166111c39092919063ffffffff16565b8061176481611e8a565b9150506114a5565b505092915050565b801561121b577f00000000000000000000000069696969696969696969696969696969696969696001600160a01b0316836001600160a01b0316036118ba57604051632e1a7d4d60e01b8152600481018290527f00000000000000000000000069696969696969696969696969696969696969696001600160a01b031690632e1a7d4d90602401600060405180830381600087803b15801561181557600080fd5b505af1158015611829573d6000803e3d6000fd5b5050604080516000808252602082019283905293506001600160a01b038616925084916118569190611e5b565b60006040518083038185875af1925050503d8060008114611893576040519150601f19603f3d011682016040523d82523d6000602084013e611898565b606091505b50509050806113a35760405163b12d13eb60e01b815260040160405180910390fd5b61121b6001600160a01b03841683836111c3565b6000806000846001600160a01b0316846040516118eb9190611e5b565b6000604051808303816000865af19150503d8060008114611928576040519150601f19603f3d011682016040523d82523d6000602084013e61192d565b606091505b50915091508180156119575750805115806119575750808060200190518101906119579190611e39565b80156111b85750505050506001600160a01b03163b151590565b606061197f83836000611986565b9392505050565b6060814710156119ab573060405163cd78605960e01b815260040161067a9190611ab5565b600080856001600160a01b031684866040516119c79190611e5b565b60006040518083038185875af1925050503d8060008114611a04576040519150601f19603f3d011682016040523d82523d6000602084013e611a09565b606091505b5091509150611a19868383611a23565b9695505050505050565b606082611a3857611a3382611a76565b61197f565b8151158015611a4f57506001600160a01b0384163b155b15611a6f5783604051639996b31560e01b815260040161067a9190611ab5565b508061197f565b805115611a865780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b634e487b7160e01b600052600160045260246000fd5b6001600160a01b0391909116815260200190565b6001600160a01b038116811461102057600080fd5b600060208284031215611af057600080fd5b813561197f81611ac9565b60008060008060808587031215611b1157600080fd5b8435611b1c81611ac9565b93506020850135611b2c81611ac9565b9250604085013591506060850135611b4381611ac9565b939692955090935050565b600081518084526020808501945080840160005b83811015611b9257815180516001600160a01b031688528301518388015260409096019590820190600101611b62565b509495945050505050565b8281526040602082015260006112846040830184611b4e565b60008060008060808587031215611bcc57600080fd5b8435611bd781611ac9565b93506020850135611be781611ac9565b93969395505050506040820135916060013590565b600060208284031215611c0e57600080fd5b813561ffff8116811461197f57600080fd5b60008060008060808587031215611c3657600080fd5b8435611c4181611ac9565b9350602085013592506040850135611c5881611ac9565b9396929550929360600135925050565b600a8110611c8657634e487b7160e01b600052602160045260246000fd5b9052565b6001600160a01b0386811682528581166020830152604082018590528316606082015260a08101611a196080830184611c68565b600060208284031215611cd057600080fd5b5051919050565b6001600160a01b03939093168352602083019190915261ffff16604082015260600190565b600060208284031215611d0e57600080fd5b815161197f81611ac9565b60018060a01b038716815285602082015284604082015283606082015282608082015260c060a08201526000611d5260c0830184611b4e565b98975050505050505050565b6001600160a01b0386811682526020820186905284811660408301528316606082015260a08101611a196080830184611c68565b634e487b7160e01b600052601160045260246000fd5b818103818111156111bd576111bd611d92565b6001600160a01b03929092168252602082015260400190565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b80820281158282048414176111bd576111bd611d92565b600082611e3457634e487b7160e01b600052601260045260246000fd5b500490565b600060208284031215611e4b57600080fd5b8151801515811461197f57600080fd5b6000825160005b81811015611e7c5760208186018101518583015201611e62565b506000920191825250919050565b600060018201611e9c57611e9c611d92565b506001019056fea2646970667358221220a3aa4be3533f21a6e26311cd954b0f508c1871c08deacb9e90b2a2207ad83c0e64736f6c63430008140033

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

00000000000000000000000062137cec7754aa3f530c98866a86dba36d2687170000000000000000000000006969696969696969696969696969696969696969000000000000000000000000fcbd14dc51f0a4d49d5e53c2e0950e0bc26d0dce000000000000000000000000b8ba8721a5bfcf828a46c5f839c559988d8d5ead000000000000000000000000609e7b64a2970157789ae6e396de1b0b8e393b050000000000000000000000001cfe31bfa1ac9b28588c91bb4300a1ed032f069f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

-----Decoded View---------------
Arg [0] : governanceAddress (address): 0x62137cEc7754aA3f530c98866a86dba36d268717
Arg [1] : wrappedNative (address): 0x6969696969696969696969696969696969696969
Arg [2] : stableCoin (address): 0xFCBD14DC51f0A4d49d5E53C2E0950e0bC26d0Dce
Arg [3] : swapRouter (address): 0xb8bA8721A5bFcF828A46C5F839c559988d8d5eaD
Arg [4] : lpRouter (address): 0x609E7b64a2970157789AE6E396de1b0b8E393b05
Arg [5] : feeRecipient (address): 0x1cfe31Bfa1Ac9b28588C91BB4300A1ED032F069F
Arg [6] : zapInFee (uint16): 0
Arg [7] : zapOutFee (uint16): 0

-----Encoded View---------------
8 Constructor Arguments found :
Arg [0] : 00000000000000000000000062137cec7754aa3f530c98866a86dba36d268717
Arg [1] : 0000000000000000000000006969696969696969696969696969696969696969
Arg [2] : 000000000000000000000000fcbd14dc51f0a4d49d5e53c2e0950e0bc26d0dce
Arg [3] : 000000000000000000000000b8ba8721a5bfcf828a46c5f839c559988d8d5ead
Arg [4] : 000000000000000000000000609e7b64a2970157789ae6e396de1b0b8e393b05
Arg [5] : 0000000000000000000000001cfe31bfa1ac9b28588c91bb4300a1ed032f069f
Arg [6] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [7] : 0000000000000000000000000000000000000000000000000000000000000000


Block Transaction Gas Used Reward
view all blocks produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
[ Download: CSV Export  ]
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.