BERA Price: $3.70 (+0.52%)

Contract

0xbEDc2b73feBa033Cc2b6F7FC20b5a474CD5e6b4f

Overview

BERA Balance

Berachain LogoBerachain LogoBerachain Logo0 BERA

BERA Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Zap In40880462025-04-23 13:30:2210 mins ago1745415022IN
0xbEDc2b73...4CD5e6b4f
0 BERA00.00000007
Zap Out40875822025-04-23 13:15:0225 mins ago1745414102IN
0xbEDc2b73...4CD5e6b4f
0 BERA0.000372940.5
Zap In40860202025-04-23 12:23:421 hr ago1745411022IN
0xbEDc2b73...4CD5e6b4f
0 BERA00.00000006
Zap Out40859442025-04-23 12:21:121 hr ago1745410872IN
0xbEDc2b73...4CD5e6b4f
0 BERA0.000110220.15
Zap In40858562025-04-23 12:18:181 hr ago1745410698IN
0xbEDc2b73...4CD5e6b4f
0 BERA0.000087650.5
Zap Out40849552025-04-23 11:48:411 hr ago1745408921IN
0xbEDc2b73...4CD5e6b4f
0 BERA0.000182440.5
Zap In40846972025-04-23 11:40:102 hrs ago1745408410IN
0xbEDc2b73...4CD5e6b4f
0 BERA0.000026680.15
Zap Out40829812025-04-23 10:43:452 hrs ago1745405025IN
0xbEDc2b73...4CD5e6b4f
0 BERA0.000362960.5
Zap In40827752025-04-23 10:37:033 hrs ago1745404623IN
0xbEDc2b73...4CD5e6b4f
0 BERA00.00000057
Zap In40824392025-04-23 10:26:033 hrs ago1745403963IN
0xbEDc2b73...4CD5e6b4f
106.03153188 BERA0.000055850.15000001
Zap In40816632025-04-23 10:00:363 hrs ago1745402436IN
0xbEDc2b73...4CD5e6b4f
0 BERA00.000003
Zap In40812072025-04-23 9:45:403 hrs ago1745401540IN
0xbEDc2b73...4CD5e6b4f
0 BERA00.00000008
Zap In40811722025-04-23 9:44:303 hrs ago1745401470IN
0xbEDc2b73...4CD5e6b4f
0 BERA00.00000006
Zap In40811182025-04-23 9:42:453 hrs ago1745401365IN
0xbEDc2b73...4CD5e6b4f
0 BERA00.00000007
Zap In40810372025-04-23 9:40:064 hrs ago1745401206IN
0xbEDc2b73...4CD5e6b4f
0.53282176 BERA00.00000005
Zap In40810242025-04-23 9:39:414 hrs ago1745401181IN
0xbEDc2b73...4CD5e6b4f
0 BERA00.00000006
Zap In40783392025-04-23 8:11:245 hrs ago1745395884IN
0xbEDc2b73...4CD5e6b4f
0 BERA00.00000007
Zap In40777642025-04-23 7:52:315 hrs ago1745394751IN
0xbEDc2b73...4CD5e6b4f
0 BERA0.000000190.001201
Zap Out40775392025-04-23 7:45:065 hrs ago1745394306IN
0xbEDc2b73...4CD5e6b4f
0 BERA00.00000007
Zap Out40774612025-04-23 7:42:285 hrs ago1745394148IN
0xbEDc2b73...4CD5e6b4f
0 BERA00.00000007
Zap In40758732025-04-23 6:49:576 hrs ago1745390997IN
0xbEDc2b73...4CD5e6b4f
0 BERA00.00000007
Zap In40756862025-04-23 6:43:506 hrs ago1745390630IN
0xbEDc2b73...4CD5e6b4f
0 BERA0.000000190.001201
Zap Out40744142025-04-23 6:01:577 hrs ago1745388117IN
0xbEDc2b73...4CD5e6b4f
0 BERA00.00000008
Zap In40744082025-04-23 6:01:447 hrs ago1745388104IN
0xbEDc2b73...4CD5e6b4f
0 BERA0.000000190.001201
Zap In40743962025-04-23 6:01:207 hrs ago1745388080IN
0xbEDc2b73...4CD5e6b4f
0 BERA00.000001
View all transactions

Latest 25 internal transactions (View All)

Parent Transaction Hash Block From To
40875822025-04-23 13:15:0225 mins ago1745414102
0xbEDc2b73...4CD5e6b4f
69.57456554 BERA
40875822025-04-23 13:15:0225 mins ago1745414102
0xbEDc2b73...4CD5e6b4f
69.57456554 BERA
40859442025-04-23 12:21:121 hr ago1745410872
0xbEDc2b73...4CD5e6b4f
13.46522399 BERA
40859442025-04-23 12:21:121 hr ago1745410872
0xbEDc2b73...4CD5e6b4f
13.46522399 BERA
40829812025-04-23 10:43:452 hrs ago1745405025
0xbEDc2b73...4CD5e6b4f
15.43410758 BERA
40829812025-04-23 10:43:452 hrs ago1745405025
0xbEDc2b73...4CD5e6b4f
15.43410758 BERA
40824392025-04-23 10:26:033 hrs ago1745403963
0xbEDc2b73...4CD5e6b4f
106.03153188 BERA
40810372025-04-23 9:40:064 hrs ago1745401206
0xbEDc2b73...4CD5e6b4f
0.53282176 BERA
40775392025-04-23 7:45:065 hrs ago1745394306
0xbEDc2b73...4CD5e6b4f
0.22068969 BERA
40775392025-04-23 7:45:065 hrs ago1745394306
0xbEDc2b73...4CD5e6b4f
0.22068969 BERA
40774612025-04-23 7:42:285 hrs ago1745394148
0xbEDc2b73...4CD5e6b4f
0.25121323 BERA
40774612025-04-23 7:42:285 hrs ago1745394148
0xbEDc2b73...4CD5e6b4f
0.25121323 BERA
40744142025-04-23 6:01:577 hrs ago1745388117
0xbEDc2b73...4CD5e6b4f
15.28669426 BERA
40744142025-04-23 6:01:577 hrs ago1745388117
0xbEDc2b73...4CD5e6b4f
15.28669426 BERA
40729442025-04-23 5:13:438 hrs ago1745385223
0xbEDc2b73...4CD5e6b4f
5.51510027 BERA
40729442025-04-23 5:13:438 hrs ago1745385223
0xbEDc2b73...4CD5e6b4f
5.51510027 BERA
40699062025-04-23 3:33:3610 hrs ago1745379216
0xbEDc2b73...4CD5e6b4f
55.74566427 BERA
40699062025-04-23 3:33:3610 hrs ago1745379216
0xbEDc2b73...4CD5e6b4f
55.74566427 BERA
40673072025-04-23 2:07:4811 hrs ago1745374068
0xbEDc2b73...4CD5e6b4f
15.57370194 BERA
40673072025-04-23 2:07:4811 hrs ago1745374068
0xbEDc2b73...4CD5e6b4f
15.57370194 BERA
40672812025-04-23 2:06:5711 hrs ago1745374017
0xbEDc2b73...4CD5e6b4f
15.50131005 BERA
40672812025-04-23 2:06:5711 hrs ago1745374017
0xbEDc2b73...4CD5e6b4f
15.50131005 BERA
40671882025-04-23 2:03:5511 hrs ago1745373835
0xbEDc2b73...4CD5e6b4f
15.71670323 BERA
40671882025-04-23 2:03:5511 hrs ago1745373835
0xbEDc2b73...4CD5e6b4f
15.71670323 BERA
40660082025-04-23 1:25:1312 hrs ago1745371513
0xbEDc2b73...4CD5e6b4f
74.7838085 BERA
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
InfraredZapper

Compiler Version
v0.8.20+commit.a1b79de6

Optimization Enabled:
Yes with 100 runs

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

import {ZapperBase} from "./ZapperBase.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 {IDexType} from "../interfaces/IDexType.sol";
import {ILpRouter} from "../interfaces/ILpRouter.sol";
import {SphereXProtected} from "@spherex-xyz/contracts/src/SphereXProtected.sol";
import {IBeraPool, IBeraVault, IAsset} from "../interfaces/exchange/Beraswap.sol";

/**
 * @title InfraredZapper
 * @notice A zapper contract that enables single-token deposits and withdrawals for Infrared Strategy
 */
contract InfraredZapper is ZapperBase {
  using SafeERC20 for IERC20;

  mapping(address => bool) public isAssetSingleToken;
  mapping(address => IDexType.DexType) public assetDex;

  event AssetInfoSet(address asset, bool isSingleToken, IDexType.DexType dex);

  constructor(
    address devAddress,
    address wrappedNative,
    address stablecoin,
    address swapRouter,
    address lpRouter,
    address feeRecipient,
    uint16 zapInFee,
    uint16 zapOutFee
  ) ZapperBase(devAddress, 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
    sphereXGuardPublic(0x07c1e85d, 0x3218e83a)
    returns (uint256 tokenOutAmount, ReturnedAsset[] memory returnedAssets)
  {
    // Transfer tokens if needed
    if (recipient != address(this)) {
      tokenInAmount = _safeTransferFromTokens(tokenIn, tokenInAmount);
    }

    address[] memory tokens;
    if (isAssetSingleToken[asset]) {
      IERC20(tokenIn).safeTransfer(address(swapRouter), tokenInAmount);
      tokenOutAmount = swapRouter.swapWithDefaultDex(address(tokenIn), asset, tokenInAmount, 0, recipient);
      tokens = new address[](1);
      tokens[0] = address(tokenIn);
      returnedAssets = _returnAssets(tokens);
      return (tokenOutAmount, returnedAssets);
    }

    return _swapToAssetsLp(asset, tokenIn, tokenInAmount, recipient);
  }

  /**
   * @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 _swapToAssetsLp(
    address asset,
    address tokenIn,
    uint256 tokenInAmount,
    address recipient
  ) internal returns (uint256 tokenOutAmount, ReturnedAsset[] memory returnedAssets) {
    // Add liquidity
    IERC20(tokenIn).safeTransfer(address(lpRouter), tokenInAmount);
    tokenOutAmount = lpRouter.addLiquidity(asset, tokenIn, tokenInAmount, recipient, assetDex[asset]);

    // Return assets
    address[] memory checkTokens = new address[](1);
    checkTokens[0] = tokenIn;
    returnedAssets = _returnAssets(checkTokens);
  }

  /**
   * @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
    sphereXGuardPublic(0x362279f7, 0xf99e6387)
    returns (uint256 tokenOutAmount, ReturnedAsset[] memory returnedAssets)
  {
    // Transfer tokens if needed
    if (recipient != address(this)) {
      assetsInAmount = _safeTransferFromTokens(asset, assetsInAmount);
    }

    if (isAssetSingleToken[asset]) {
      IERC20(asset).safeTransfer(address(swapRouter), assetsInAmount);
      tokenOutAmount = swapRouter.swapWithDefaultDex(asset, tokenOut, assetsInAmount, 0, recipient);
      address[] memory tokens = new address[](1);
      tokens[0] = asset;
      returnedAssets = _returnAssets(tokens);
      return (tokenOutAmount, returnedAssets);
    }

    return _swapFromAssetsLp(asset, tokenOut, assetsInAmount, recipient);
  }

  /**
   * @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 _swapFromAssetsLp(
    address asset,
    address tokenOut,
    uint256 assetsInAmount,
    address recipient
  ) internal returns (uint256 tokenOutAmount, ReturnedAsset[] memory returnedAssets) {
    // Remove liquidity
    IERC20(asset).safeTransfer(address(lpRouter), assetsInAmount);
    tokenOutAmount = lpRouter.removeLiquidity(asset, assetsInAmount, recipient, tokenOut, assetDex[asset]);

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

    address[] memory checkTokens = new address[](1);
    checkTokens[0] = address(0);
    returnedAssets = _returnAssets(checkTokens);
  }

  function setAssetInfo(address asset, bool isSingleToken, IDexType.DexType dex) external onlyGovernance {
    _revertAddressZero(asset);
    isAssetSingleToken[asset] = isSingleToken;
    assetDex[asset] = dex;
    emit AssetInfoSet(asset, isSingleToken, dex);
  }
}

File 2 of 20 : 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 20 : 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 20 : 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 20 : 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 20 : 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 20 : 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 20 : 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 20 : ISphereXEngine.sol
// SPDX-License-Identifier: UNLICENSED
// (c) SphereX 2023 Terms&Conditions

pragma solidity ^0.8.0;

/**
 * @title Interface for SphereXEngine - definitions of core functionality
 * @author SphereX Technologies ltd
 * @notice This interface is imported by SphereXProtected, so that SphereXProtected can call functions from SphereXEngine
 * @dev Full docs of these functions can be found in SphereXEngine
 */
interface ISphereXEngine {
    function sphereXValidatePre(int256 num, address sender, bytes calldata data) external returns (bytes32[] memory);
    function sphereXValidatePost(
        int256 num,
        uint256 gas,
        bytes32[] calldata valuesBefore,
        bytes32[] calldata valuesAfter
    ) external;
    function sphereXValidateInternalPre(int256 num) external returns (bytes32[] memory);
    function sphereXValidateInternalPost(
        int256 num,
        uint256 gas,
        bytes32[] calldata valuesBefore,
        bytes32[] calldata valuesAfter
    ) external;

    function addAllowedSenderOnChain(address sender) external;

    /**
     * This function is taken as is from OZ IERC165, we don't inherit from OZ
     * to avoid collisions with the customer OZ version.
     *
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

/**
 * @dev this struct is used to reduce the stack usage of the modifiers.
 */
struct ModifierLocals {
    bytes32[] storageSlots;
    bytes32[] valuesBefore;
    uint256 gas;
    address engine;
}

File 10 of 20 : SphereXConfiguration.sol
// SPDX-License-Identifier: UNLICENSED
// (c) SphereX 2023 Terms&Conditions

pragma solidity ^0.8.0;

import {ISphereXEngine, ModifierLocals} from "./ISphereXEngine.sol";

/**
 * @title SphereX base Customer contract template
 */
/// @custom:oz-upgrades-unsafe-allow constructor
abstract contract SphereXConfiguration {
    /**
     * @dev we would like to avoid occupying storage slots
     * @dev to easily incorporate with existing contracts
     */
    bytes32 private constant SPHEREX_ADMIN_STORAGE_SLOT = bytes32(uint256(keccak256("eip1967.spherex.spherex")) - 1);
    bytes32 private constant SPHEREX_PENDING_ADMIN_STORAGE_SLOT =
        bytes32(uint256(keccak256("eip1967.spherex.pending")) - 1);
    bytes32 private constant SPHEREX_OPERATOR_STORAGE_SLOT = bytes32(uint256(keccak256("eip1967.spherex.operator")) - 1);
    bytes32 private constant SPHEREX_ENGINE_STORAGE_SLOT =
        bytes32(uint256(keccak256("eip1967.spherex.spherex_engine")) - 1);

    event ChangedSpherexOperator(address oldSphereXAdmin, address newSphereXAdmin);
    event ChangedSpherexEngineAddress(address oldEngineAddress, address newEngineAddress);
    event SpherexAdminTransferStarted(address currentAdmin, address pendingAdmin);
    event SpherexAdminTransferCompleted(address oldAdmin, address newAdmin);
    event NewAllowedSenderOnchain(address sender);

    /**
     * @dev used when the client doesn't use a proxy
     */
    constructor(address admin, address operator, address engine) {
        __SphereXProtectedBase_init(admin, operator, engine);
    }

    /**
     * @dev used when the client uses a proxy - should be called by the inhereter initialization
     */
    function __SphereXProtectedBase_init(address admin, address operator, address engine) internal virtual {
        _setAddress(SPHEREX_ADMIN_STORAGE_SLOT, admin);
        emit SpherexAdminTransferCompleted(address(0), admin);

        _setAddress(SPHEREX_OPERATOR_STORAGE_SLOT, operator);
        emit ChangedSpherexOperator(address(0), operator);

        _checkSphereXEngine(engine);
        _setAddress(SPHEREX_ENGINE_STORAGE_SLOT, engine);
        emit ChangedSpherexEngineAddress(address(0), engine);
    }

    // ============ Helper functions ============

    function _sphereXEngine() internal view returns (ISphereXEngine) {
        return ISphereXEngine(_getAddress(SPHEREX_ENGINE_STORAGE_SLOT));
    }

    /**
     * Stores a new address in an arbitrary slot
     * @param slot where to store the address
     * @param newAddress address to store in given slot
     */
    function _setAddress(bytes32 slot, address newAddress) internal {
        // solhint-disable-next-line no-inline-assembly
        // slither-disable-next-line assembly
        assembly {
            sstore(slot, newAddress)
        }
    }

    /**
     * Returns an address from an arbitrary slot.
     * @param slot to read an address from
     */
    function _getAddress(bytes32 slot) internal view returns (address addr) {
        // solhint-disable-next-line no-inline-assembly
        // slither-disable-next-line assembly
        assembly {
            addr := sload(slot)
        }
    }

    // ============ Local modifiers ============

    modifier onlySphereXAdmin() {
        require(msg.sender == _getAddress(SPHEREX_ADMIN_STORAGE_SLOT), "SphereX error: admin required");
        _;
    }

    modifier onlySpherexOperator() {
        require(msg.sender == _getAddress(SPHEREX_OPERATOR_STORAGE_SLOT), "SphereX error: operator required");
        _;
    }

    modifier returnsIfNotActivated() {
        if (address(_sphereXEngine()) == address(0)) {
            return;
        }

        _;
    }

    // ============ Management ============

    /**
     * Returns the currently pending admin address, the one that can call acceptSphereXAdminRole to become the admin.
     * @dev Could not use OZ Ownable2Step because the client's contract might use it.
     */
    function pendingSphereXAdmin() public view returns (address) {
        return _getAddress(SPHEREX_PENDING_ADMIN_STORAGE_SLOT);
    }

    /**
     * Returns the current admin address, the one that can call acceptSphereXAdminRole to become the admin.
     * @dev Could not use OZ Ownable2Step because the client's contract might use it.
     */
    function sphereXAdmin() public view returns (address) {
        return _getAddress(SPHEREX_ADMIN_STORAGE_SLOT);
    }

    /**
     * Returns the current operator address.
     */
    function sphereXOperator() public view returns (address) {
        return _getAddress(SPHEREX_OPERATOR_STORAGE_SLOT);
    }

    /**
     * Returns the current engine address.
     */
    function sphereXEngine() public view returns (address) {
        return _getAddress(SPHEREX_ENGINE_STORAGE_SLOT);
    }

    /**
     * Setting the address of the next admin. this address will have to accept the role to become the new admin.
     * @dev Could not use OZ Ownable2Step because the client's contract might use it.
     */
    function transferSphereXAdminRole(address newAdmin) public virtual onlySphereXAdmin {
        _setAddress(SPHEREX_PENDING_ADMIN_STORAGE_SLOT, newAdmin);
        emit SpherexAdminTransferStarted(sphereXAdmin(), newAdmin);
    }

    /**
     * Accepting the admin role and completing the transfer.
     * @dev Could not use OZ Ownable2Step because the client's contract might use it.
     */
    function acceptSphereXAdminRole() public virtual {
        require(pendingSphereXAdmin() == msg.sender, "SphereX error: not the pending account");
        address oldAdmin = sphereXAdmin();
        _setAddress(SPHEREX_ADMIN_STORAGE_SLOT, msg.sender);
        _setAddress(SPHEREX_PENDING_ADMIN_STORAGE_SLOT, address(0));
        emit SpherexAdminTransferCompleted(oldAdmin, msg.sender);
    }

    /**
     *
     * @param newSphereXOperator new address of the new operator account
     */
    function changeSphereXOperator(address newSphereXOperator) external onlySphereXAdmin {
        address oldSphereXOperator = _getAddress(SPHEREX_OPERATOR_STORAGE_SLOT);
        _setAddress(SPHEREX_OPERATOR_STORAGE_SLOT, newSphereXOperator);
        emit ChangedSpherexOperator(oldSphereXOperator, newSphereXOperator);
    }

    /**
     * Checks the given address implements ISphereXEngine or is address(0)
     * @param newSphereXEngine new address of the spherex engine
     */
    function _checkSphereXEngine(address newSphereXEngine) private view {
        require(
            newSphereXEngine == address(0)
                || ISphereXEngine(newSphereXEngine).supportsInterface(type(ISphereXEngine).interfaceId),
            "SphereX error: not a SphereXEngine"
        );
    }

    /**
     *
     * @param newSphereXEngine new address of the spherex engine
     * @dev this is also used to actually enable the defense
     * (because as long is this address is 0, the protection is disabled).
     */
    function changeSphereXEngine(address newSphereXEngine) external onlySpherexOperator {
        _checkSphereXEngine(newSphereXEngine);
        address oldEngine = _getAddress(SPHEREX_ENGINE_STORAGE_SLOT);
        _setAddress(SPHEREX_ENGINE_STORAGE_SLOT, newSphereXEngine);
        emit ChangedSpherexEngineAddress(oldEngine, newSphereXEngine);
    }
    // ============ Engine interaction ============

    function _addAllowedSenderOnChain(address newSender) internal {
        ISphereXEngine engine = _sphereXEngine();
        if (address(engine) != address(0)) {
            engine.addAllowedSenderOnChain(newSender);
            emit NewAllowedSenderOnchain(newSender);
        }
    }
}

File 11 of 20 : SphereXProtected.sol
// SPDX-License-Identifier: UNLICENSED
// (c) SphereX 2023 Terms&Conditions

pragma solidity ^0.8.0;

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

/**
 * @title SphereX base Customer contract template
 * @dev notice this is an abstract
 */
abstract contract SphereXProtected is SphereXProtectedBase(msg.sender, address(0), address(0)) {}

File 12 of 20 : SphereXProtectedBase.sol
// SPDX-License-Identifier: UNLICENSED
// (c) SphereX 2023 Terms&Conditions

pragma solidity ^0.8.0;

import {ISphereXEngine, ModifierLocals} from "./ISphereXEngine.sol";
import {SphereXConfiguration} from "./SphereXConfiguration.sol";

/**
 * @title SphereX base Customer contract template
 */
/// @custom:oz-upgrades-unsafe-allow constructor 
abstract contract SphereXProtectedBase is SphereXConfiguration {
    constructor(address admin, address operator, address engine) SphereXConfiguration(admin, operator, engine) {}

    // ============ Hooks ============

    /**
     * @dev internal function for engine communication. We use it to reduce contract size.
     *  Should be called before the code of a function.
     * @param num function identifier
     * @param isExternalCall set to true if this was called externally
     *  or a 'public' function from another address
     */
    function _sphereXValidatePre(int256 num, bool isExternalCall)
        private
        returnsIfNotActivated
        returns (ModifierLocals memory locals)
    {
        ISphereXEngine engine = _sphereXEngine();
        if (isExternalCall) {
            locals.storageSlots = engine.sphereXValidatePre(num, msg.sender, msg.data);
        } else {
            locals.storageSlots = engine.sphereXValidateInternalPre(num);
        }
        locals.valuesBefore = _readStorage(locals.storageSlots);
        locals.gas = gasleft();
        locals.engine = address(engine);
        return locals;
    }

    /**
     * @dev internal function for engine communication. We use it to reduce contract size.
     *  Should be called after the code of a function.
     * @param num function identifier
     * @param isExternalCall set to true if this was called externally
     *  or a 'public' function from another address
     */
    function _sphereXValidatePost(int256 num, bool isExternalCall, ModifierLocals memory locals) private {
        ISphereXEngine engine = ISphereXEngine(locals.engine);

        if (address(engine) == address(0)) {
            return;
        }

        uint256 gas = locals.gas - gasleft();

        bytes32[] memory valuesAfter;
        valuesAfter = _readStorage(locals.storageSlots);

        if (isExternalCall) {
            engine.sphereXValidatePost(num, gas, locals.valuesBefore, valuesAfter);
        } else {
            engine.sphereXValidateInternalPost(num, gas, locals.valuesBefore, valuesAfter);
        }
    }

    // ============ Modifiers ============

    /**
     *  @dev Modifier to be incorporated in all internal protected non-view functions
     */
    modifier sphereXGuardInternal(int256 num) {
        ModifierLocals memory locals = _sphereXValidatePre(num, false);
        _;
        _sphereXValidatePost(-num, false, locals);
    }

    /**
     *  @dev Modifier to be incorporated in all external protected non-view functions
     */
    modifier sphereXGuardExternal(int256 num) {
        ModifierLocals memory locals = _sphereXValidatePre(num, true);
        _;
        _sphereXValidatePost(-num, true, locals);
    }

    /**
     *  @dev Modifier to be incorporated in all public protected non-view functions
     */
    modifier sphereXGuardPublic(int256 num, bytes4 selector) {
        ModifierLocals memory locals = _sphereXValidatePre(num, msg.sig == selector);
        _;
        _sphereXValidatePost(-num, msg.sig == selector, locals);
    }

    // ============ Internal Storage logic ============

    /**
     * Internal function that reads values from given storage slots and returns them
     * @param storageSlots list of storage slots to read
     * @return list of values read from the various storage slots
     */
    function _readStorage(bytes32[] memory storageSlots) internal view returns (bytes32[] memory) {
        uint256 arrayLength = storageSlots.length;
        bytes32[] memory values = new bytes32[](arrayLength);
        // create the return array data

        for (uint256 i = 0; i < arrayLength; i++) {
            bytes32 slot = storageSlots[i];
            bytes32 temp_value;
            // solhint-disable-next-line no-inline-assembly
            // slither-disable-next-line assembly
            assembly {
                temp_value := sload(slot)
            }

            values[i] = temp_value;
        }
        return values;
    }
}

File 13 of 20 : 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);
}

File 14 of 20 : 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 15 of 20 : 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 16 of 20 : 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 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 17 of 20 : 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 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 view 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 view 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 18 of 20 : 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 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 19 of 20 : 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 20 of 20 : 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 {SphereXProtected} from "@spherex-xyz/contracts/src/SphereXProtected.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 SphereXProtected, 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 sphereXGuardExternal(0xd473ef3c) {
    _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 sphereXGuardExternal(0x26b2eef4) {
    _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 sphereXGuardExternal(0x5b9fdae4) {
    _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 sphereXGuardInternal(0x367a5b5d) {
    if (IERC20(tokenAddress).allowance(address(this), spenderAddress) == 0) {
      IERC20(tokenAddress).approve(spenderAddress, type(uint256).max);
    }
  }

  function _safeTransferFromTokens(
    address token,
    uint256 amount
  ) internal sphereXGuardInternal(0x9ffc4661) 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
  ) internal sphereXGuardInternal(0x65afacc1) 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, ) = msg.sender.call{value: balance}(new bytes(0));
          if (!success) revert ETHTransferFailed();
        } else {
          IERC20(tokens[i]).safeTransfer(msg.sender, 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 amount The amount of tokens returned
   */
  function _returnAsset(
    address token,
    address recipient
  ) internal sphereXGuardInternal(0xf45372f8) returns (uint256 amount) {
    amount = IERC20(token).balanceOf(address(this));
    _returnAssetWithAmount(token, recipient, amount);
  }

  /**
   * @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
    sphereXGuardExternal(0x621b094a)
    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
    sphereXGuardExternal(0xaf73b205)
    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":"devAddress","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":false,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"bool","name":"isSingleToken","type":"bool"},{"indexed":false,"internalType":"enum IDexType.DexType","name":"dex","type":"uint8"}],"name":"AssetInfoSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldEngineAddress","type":"address"},{"indexed":false,"internalType":"address","name":"newEngineAddress","type":"address"}],"name":"ChangedSpherexEngineAddress","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldSphereXAdmin","type":"address"},{"indexed":false,"internalType":"address","name":"newSphereXAdmin","type":"address"}],"name":"ChangedSpherexOperator","type":"event"},{"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":false,"internalType":"address","name":"sender","type":"address"}],"name":"NewAllowedSenderOnchain","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldAdmin","type":"address"},{"indexed":false,"internalType":"address","name":"newAdmin","type":"address"}],"name":"SpherexAdminTransferCompleted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"currentAdmin","type":"address"},{"indexed":false,"internalType":"address","name":"pendingAdmin","type":"address"}],"name":"SpherexAdminTransferStarted","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":"acceptSphereXAdminRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"assetDex","outputs":[{"internalType":"enum IDexType.DexType","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newSphereXEngine","type":"address"}],"name":"changeSphereXEngine","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newSphereXOperator","type":"address"}],"name":"changeSphereXOperator","outputs":[],"stateMutability":"nonpayable","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":[{"internalType":"address","name":"","type":"address"}],"name":"isAssetSingleToken","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lpRouter","outputs":[{"internalType":"contract ILpRouter","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingSphereXAdmin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"bool","name":"isSingleToken","type":"bool"},{"internalType":"enum IDexType.DexType","name":"dex","type":"uint8"}],"name":"setAssetInfo","outputs":[],"stateMutability":"nonpayable","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":"sphereXAdmin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"sphereXEngine","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"sphereXOperator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","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":[{"internalType":"address","name":"newAdmin","type":"address"}],"name":"transferSphereXAdminRole","outputs":[],"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"}]

60a06040523480156200001157600080fd5b50604051620037e8380380620037e8833981016040819052620000349162000566565b87878787878787873360008082818062000050838280620002de565b50506001600055505050506001600160a01b03881615806200007957506001600160a01b038716155b806200008c57506001600160a01b038616155b806200009f57506001600160a01b038516155b80620000b257506001600160a01b038416155b80620000c557506001600160a01b038316155b15620000e45760405163d92e233d60e01b815260040160405180910390fd5b600280546001600160a01b0319166001600160a01b038a8116919091179091558716608081905260408051630d0e30db60e41b8152905163d0e30db0916000916004808301928492919082900301818388803b1580156200014457600080fd5b505af115801562000159573d6000803e3d6000fd5b5050608051604051632e1a7d4d60e01b8152600060048201526001600160a01b039091169350632e1a7d4d92506024019050600060405180830381600087803b158015620001a657600080fd5b505af1158015620001bb573d6000803e3d6000fd5b5050600180546001600160a01b03808b1664010000000002600160201b600160c01b031990921691909117909155600380548983166001600160a01b031991821617909155600480549289169290911691909117905550506103e861ffff831611156200024e57604051633c057d7b60e01b815261ffff831660048201526103e860248201526044015b60405180910390fd5b6103e861ffff821611156200028557604051633c057d7b60e01b815261ffff821660048201526103e8602482015260440162000245565b600580546001600160a01b039094166001600160a01b0319909416939093179092556001805461ffff938416620100000263ffffffff19909116939092169290921717905550620006619b505050505050505050505050565b620003146200030f60017fca334bf49ef20e9cbff039feda3bc1c2a853aff17b1b5187e4aa1380ec55829d6200060e565b849055565b60408051600081526001600160a01b03851660208201527f67ebaebcd2ca5a91a404e898110f221747e8d15567f2388a34794aab151cf3e6910160405180910390a16200038c6200038760017fadf90d75f0f2d657d5a22fa0f6e4dabb83c4598b77158eb4119cb38f1d8644636200060e565b839055565b60408051600081526001600160a01b03841660208201527f2ac55ae7ba47db34b5334622acafeb34a65daf143b47019273185d64c73a35a5910160405180910390a1620003d98162000456565b6200040f6200040a60017f1777adabd324f814e2b0e28f6edf876dce01d7d66358c9acfe87b1b5f38338d66200060e565b829055565b60408051600081526001600160a01b03831660208201527ff33499cccaa0611882086224cc48cd82ef54b66a4d2edf4ed67108dd516896d5910160405180910390a1505050565b6001600160a01b0381161580620004da57506040516301ffc9a760e01b81526329f20dd560e11b60048201526001600160a01b038216906301ffc9a790602401602060405180830381865afa158015620004b4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620004da919062000636565b620005335760405162461bcd60e51b815260206004820152602260248201527f53706865726558206572726f723a206e6f7420612053706865726558456e67696044820152616e6560f01b606482015260840162000245565b50565b80516001600160a01b03811681146200054e57600080fd5b919050565b805161ffff811681146200054e57600080fd5b600080600080600080600080610100898b0312156200058457600080fd5b6200058f8962000536565b97506200059f60208a0162000536565b9650620005af60408a0162000536565b9550620005bf60608a0162000536565b9450620005cf60808a0162000536565b9350620005df60a08a0162000536565b9250620005ef60c08a0162000553565b9150620005ff60e08a0162000553565b90509295985092959890939650565b818103818111156200063057634e487b7160e01b600052601160045260246000fd5b92915050565b6000602082840312156200064957600080fd5b815180151581146200065a57600080fd5b9392505050565b608051613133620006b5600039600081816101c2015281816105a8015281816109c501528181610a390152818161120401528181611ef401528181611f5a015281816126a601526126f301526131336000f3fe6080604052600436106101b25760003560e01c80639aa9019e116100ed578063cda5873e11610090578063cda5873e146104e3578063d55be8c614610503578063daba7ef714610519578063e74b981b14610539578063e7e5db4f14610559578063e9cbd8221461056e578063eb6d3a1114610596578063f99e6387146105ca578063fecc657c146105ea57600080fd5b80639aa9019e146103f6578063a863ff2f1461040b578063a89635bc1461042b578063ab033ea914610446578063afb44e3714610466578063bc063e1a1461026c578063c31c9c0714610486578063c64e74e2146104a657600080fd5b80634690484011610155578063469048401461030b5780634c6c848f1461032b5780634ee8f8581461034b5780635aa6e6751461036b5780638426b9681461038b5780638f0af424146103a05780639739c03e146103c057806399e80f3b146103e157600080fd5b80630c8106dc146101f657806315a292031461022c57806323af4e171461024c578063257d9bb81461026c5780633218e83a146102955780633c231166146102c35780633c3b1ef6146102d857806341273657146102eb57600080fd5b366101f157336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146101ef576101ef6129ce565b005b600080fd5b34801561020257600080fd5b50600454610216906001600160a01b031681565b60405161022391906129e4565b60405180910390f35b34801561023857600080fd5b506101ef610247366004612a0d565b61062a565b34801561025857600080fd5b506101ef610267366004612a0d565b6106da565b34801561027857600080fd5b506102826103e881565b60405161ffff9091168152602001610223565b3480156102a157600080fd5b506102b56102b0366004612a2a565b61078d565b604051610223929190612acc565b3480156102cf57600080fd5b50610216610939565b6102b56102e6366004612aed565b610960565b3480156102f757600080fd5b506101ef610306366004612a0d565b610d13565b34801561031757600080fd5b50600554610216906001600160a01b031681565b34801561033757600080fd5b506101ef610346366004612a0d565b610db6565b34801561035757600080fd5b506101ef610366366004612a0d565b610eb9565b34801561037757600080fd5b50600254610216906001600160a01b031681565b34801561039757600080fd5b506101ef610f72565b3480156103ac57600080fd5b506101ef6103bb366004612b33565b61106b565b3480156103cc57600080fd5b506001546102829062010000900461ffff1681565b3480156103ed57600080fd5b5061021661111b565b34801561040257600080fd5b50610216611139565b34801561041757600080fd5b506102b5610426366004612b57565b611157565b34801561043757600080fd5b506001546102829061ffff1681565b34801561045257600080fd5b506101ef610461366004612a0d565b6113cf565b34801561047257600080fd5b506101ef610481366004612b33565b61144f565b34801561049257600080fd5b50600354610216906001600160a01b031681565b3480156104b257600080fd5b506104d66104c1366004612a0d565b60076020526000908152604090205460ff1681565b6040516102239190612bd7565b3480156104ef57600080fd5b506101ef6104fe366004612bf3565b61150b565b34801561050f57600080fd5b5061028261271081565b34801561052557600080fd5b506101ef610534366004612a0d565b6115cf565b34801561054557600080fd5b506101ef610554366004612a0d565b611674565b34801561056557600080fd5b506102166116fa565b34801561057a57600080fd5b506001546102169064010000000090046001600160a01b031681565b3480156105a257600080fd5b506102167f000000000000000000000000000000000000000000000000000000000000000081565b3480156105d657600080fd5b506102b56105e5366004612a2a565b611718565b3480156105f657600080fd5b5061061a610605366004612a0d565b60066020526000908152604090205460ff1681565b6040519015158152602001610223565b6002546001600160a01b0316331461065557604051632d5be4cb60e21b815260040160405180910390fd5b6326b2eef4600061066782600161186d565b9050610672836119e9565b600480546001600160a01b038581166001600160a01b0319831681179093556040519116919082907f82c52d8aa62200dfc997bdf1100b1c2ed614580941b2b8637d10b8ca0428eeec90600090a3506106d56106cd83612c58565b600183611a13565b505050565b6002546001600160a01b0316331461070557604051632d5be4cb60e21b815260040160405180910390fd5b635b9fdae4600061071782600161186d565b9050610722836119e9565b60018054640100000000600160c01b0319166401000000006001600160a01b03868116828102939093179384905560405192939190910416907f497b8419a37c8014b1a755ec707f25d11ae4bade591d379f9a4095f37edaf76e90600090a36106d56106cd83612c58565b600060606307c1e85d63190c741d60e11b836107b68382356001600160e01b031916841461186d565b90506001600160a01b03861630146107d5576107d28888611b35565b96505b6001600160a01b03891660009081526006602052604090205460609060ff16156108f657600354610813906001600160a01b038b811691168a611cfe565b6003546040516305c13d4360e11b81526001600160a01b0390911690630b827a869061084c908c908e908d906000908e90600401612c74565b6020604051808303816000875af115801561086b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061088f9190612ca4565b6040805160018082528183019092529197506020808301908036833701905050905088816000815181106108c5576108c5612cd3565b60200260200101906001600160a01b031690816001600160a01b0316815250506108ee81611d56565b945050610908565b6109028a8a8a8a6120a7565b95509550505b61092d61091484612c58565b6000356001600160e01b03199081169085161483611a13565b50505094509492505050565b600061095b61095760016000805160206130de833981519152612ce9565b5490565b905090565b6000606061096c6121c8565b63621b094a600061097e82600161186d565b90506001600160a01b038716610a62576103e83410156109c3576000346103e8604051630b722aef60e21b81526004016109ba93929190612cfc565b60405180910390fd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d0e30db0346040518263ffffffff1660e01b81526004016000604051808303818588803b158015610a1e57600080fd5b505af1158015610a32573d6000803e3d6000fd5b50505050507f00000000000000000000000000000000000000000000000000000000000000009650349550610a9a565b6103e8861015610a8d5786866103e8604051630b722aef60e21b81526004016109ba93929190612cfc565b610a978787611b35565b95505b600154600090610ab090899061ffff16896121f2565b80925081985050506000879050886001600160a01b03168a6001600160a01b03166338d52e0f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610b05573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b299190612d21565b6001600160a01b031614610ba957610ba48a6001600160a01b03166338d52e0f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610b78573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b9c9190612d21565b8a8a3061078d565b955090505b610c208a828c6001600160a01b03166338d52e0f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610bec573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c109190612d21565b6001600160a01b0316919061224d565b60405163bc157ac160e01b815260048101829052336024820152604481018890526001600160a01b038b169063bc157ac1906064016020604051808303816000875af1158015610c74573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c989190612ca4565b9550896001600160a01b0316336001600160a01b03167f87640c2e40d67703e350e7d24d4e416e5c4f1e1550e1285b1bf667440d36bdcf8b8b858b888c604051610ce796959493929190612d3e565b60405180910390a35050610cfe826106cd90612c58565b5050610d0a6001600055565b94509492505050565b6002546001600160a01b03163314610d3e57604051632d5be4cb60e21b815260040160405180910390fd5b63d473ef3c6000610d5082600161186d565b9050610d5b836119e9565b600380546001600160a01b038581166001600160a01b0319831681179093556040519116919082907f5c5b0bb5085481c320dc523e3b3f829eaa807936f615add0077fa213f345090e90600090a3506106d56106cd83612c58565b610dd2610957600160008051602061307e833981519152612ce9565b6001600160a01b0316336001600160a01b031614610e325760405162461bcd60e51b815260206004820181905260248201527f53706865726558206572726f723a206f70657261746f7220726571756972656460448201526064016109ba565b610e3b816122e5565b6000610e5961095760016000805160206130de833981519152612ce9565b9050610e7c610e7760016000805160206130de833981519152612ce9565b839055565b7ff33499cccaa0611882086224cc48cd82ef54b66a4d2edf4ed67108dd516896d58183604051610ead929190612d83565b60405180910390a15050565b610ed561095760016000805160206130be833981519152612ce9565b6001600160a01b0316336001600160a01b031614610f055760405162461bcd60e51b81526004016109ba90612d9d565b6000610f23610957600160008051602061307e833981519152612ce9565b9050610f41610e77600160008051602061307e833981519152612ce9565b7f2ac55ae7ba47db34b5334622acafeb34a65daf143b47019273185d64c73a35a58183604051610ead929190612d83565b33610f7b611139565b6001600160a01b031614610fe05760405162461bcd60e51b815260206004820152602660248201527f53706865726558206572726f723a206e6f74207468652070656e64696e67206160448201526518d8dbdd5b9d60d21b60648201526084016109ba565b6000610fea61111b565b905061100d61100860016000805160206130be833981519152612ce9565b339055565b61102f611029600160008051602061309e833981519152612ce9565b60009055565b7f67ebaebcd2ca5a91a404e898110f221747e8d15567f2388a34794aab151cf3e68133604051611060929190612d83565b60405180910390a150565b6002546001600160a01b0316331461109657604051632d5be4cb60e21b815260040160405180910390fd5b6103e861ffff821611156110cb57604051633c057d7b60e01b815261ffff821660048201526103e860248201526044016109ba565b6001805461ffff83811661ffff1983168117909355604080519190921680825260208201939093527fcd5c26e400cb366bdac1d1cbdec4b428f15bc14a133ecbda85bc6f11f7acb9f19101610ead565b600061095b61095760016000805160206130be833981519152612ce9565b600061095b610957600160008051602061309e833981519152612ce9565b600060606111636121c8565b63af73b205600061117582600161186d565b604051635d043b2960e11b8152600481018990523060248201523360448201529091506000906001600160a01b038a169063ba087652906064016020604051808303816000875af11580156111ce573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111f29190612ca4565b90506001600160a01b038716611226577f000000000000000000000000000000000000000000000000000000000000000096505b866001600160a01b0316896001600160a01b03166338d52e0f6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561126e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112929190612d21565b6001600160a01b0316036112a85780945061131b565b611315896001600160a01b03166338d52e0f6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156112e9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061130d9190612d21565b888330611718565b90955093505b600061133888600160029054906101000a900461ffff16886121f2565b909650905061134788336123bc565b508686101561138257604051632752fb4960e11b81526001600160a01b038916600482015260248101879052604481018890526064016109ba565b896001600160a01b0316336001600160a01b03167f0bc6c5ccdba0b566b3adbb7c24c419a1b872218cd487d145a4e4eb70b2c8bfb38a89868e878c604051610ce796959493929190612d3e565b6002546001600160a01b031633146113fa57604051632d5be4cb60e21b815260040160405180910390fd5b611403816119e9565b600280546001600160a01b0319166001600160a01b03831690811790915560405181907f3aaaebeb4821d6a7e5c77ece53cff0afcc56c82add2c978dbbb7f73e84cbcfd290600090a350565b6002546001600160a01b0316331461147a57604051632d5be4cb60e21b815260040160405180910390fd5b6103e861ffff821611156114af57604051633c057d7b60e01b815261ffff821660048201526103e860248201526044016109ba565b6001805461ffff8381166201000081810263ffff00001985161790945560408051949093049091168084526020840191909152917febcac71ae552a15ae4d23c933603e1a71f47ff8371bdd98194f037156b73a7199101610ead565b6002546001600160a01b0316331461153657604051632d5be4cb60e21b815260040160405180910390fd5b61153f836119e9565b6001600160a01b0383166000908152600660209081526040808320805486151560ff199182161790915560079092529091208054839216600183600981111561158a5761158a612b9f565b02179055507f3b79c840859aa9e93d93ccb4f7a8b6c425621929bcc61a03194f607a53d0f4ab8383836040516115c293929190612dd4565b60405180910390a1505050565b6115eb61095760016000805160206130be833981519152612ce9565b6001600160a01b0316336001600160a01b03161461161b5760405162461bcd60e51b81526004016109ba90612d9d565b61163c611637600160008051602061309e833981519152612ce9565b829055565b7f5778f1547abbbb86090a43c32aec38334b31df4beeb6f8f3fa063f593b53a52661166561111b565b82604051611060929190612d83565b6002546001600160a01b0316331461169f57604051632d5be4cb60e21b815260040160405180910390fd5b6116a8816119e9565b600580546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f0bc21fe5c3ab742ff1d15b5c4477ffbacf1167e618228078fa625edebe7f331d90600090a35050565b600061095b610957600160008051602061307e833981519152612ce9565b6000606063362279f763f99e638760e01b836117418382356001600160e01b031916841461186d565b90506001600160a01b03861630146117605761175d8988611b35565b96505b6001600160a01b03891660009081526006602052604090205460ff16156118515760035461179b906001600160a01b038b8116911689611cfe565b6003546040516305c13d4360e11b81526001600160a01b0390911690630b827a86906117d4908c908c908c906000908d90600401612c74565b6020604051808303816000875af11580156117f3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118179190612ca4565b604080516001808252818301909252919650600091906020808301908036833701905050905089816000815181106108c5576108c5612cd3565b61185d8989898961244d565b9450945061092d61091484612c58565b6118a1604051806080016040528060608152602001606081526020016000815260200160006001600160a01b031681525090565b60006118ab610939565b6001600160a01b0316146119e35760006118c3610939565b9050821561194c57604051634492e52d60e11b81526001600160a01b03821690638925ca5a906118fe90879033906000903690600401612df9565b6000604051808303816000875af115801561191d573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526119459190810190612e40565b82526119be565b604051633e88494360e01b8152600481018590526001600160a01b03821690633e884943906024016000604051808303816000875af1158015611993573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526119bb9190810190612e40565b82525b81516119c990612554565b60208301525a60408301526001600160a01b031660608201525b92915050565b6001600160a01b038116611a105760405163d92e233d60e01b815260040160405180910390fd5b50565b60608101516001600160a01b038116611a2c5750505050565b60005a8360400151611a3e9190612ce9565b90506060611a4f8460000151612554565b90508415611ac4576020840151604051631e17b28d60e31b81526001600160a01b0385169163f0bd946891611a8d918a918791908790600401612f22565b600060405180830381600087803b158015611aa757600080fd5b505af1158015611abb573d6000803e3d6000fd5b50505050611b2d565b602084015160405163027313cd60e11b81526001600160a01b038516916304e6279a91611afa918a918791908790600401612f22565b600060405180830381600087803b158015611b1457600080fd5b505af1158015611b28573d6000803e3d6000fd5b505050505b505050505050565b6000639ffc46616000611b4982600061186d565b905083856001600160a01b031663dd62ed3e33306040518363ffffffff1660e01b8152600401611b7a929190612d83565b602060405180830381865afa158015611b97573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bbb9190612ca4565b1015611bda576040516332da96a360e01b815260040160405180910390fd5b6040516370a0823160e01b81526000906001600160a01b038716906370a0823190611c099030906004016129e4565b602060405180830381865afa158015611c26573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c4a9190612ca4565b9050611c616001600160a01b03871633308861260b565b6040516370a0823160e01b81526000906001600160a01b038816906370a0823190611c909030906004016129e4565b602060405180830381865afa158015611cad573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cd19190612ca4565b9050611cdd8282612ce9565b945050505b611cf6611cee83612c58565b600083611a13565b505092915050565b6106d583846001600160a01b031663a9059cbb8585604051602401611d24929190612f5e565b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050612644565b60606365afacc16000611d6a82600061186d565b90506000845167ffffffffffffffff811115611d8857611d88612cbd565b604051908082528060200260200182016040528015611dcd57816020015b6040805180820190915260008082526020820152815260200190600190039081611da65790505b50935060005b85518110156120915760006001600160a01b0316868281518110611df957611df9612cd3565b60200260200101516001600160a01b0316031561207f57858181518110611e2257611e22612cd3565b60200260200101516001600160a01b03166370a08231306040518263ffffffff1660e01b8152600401611e5591906129e4565b602060405180830381865afa158015611e72573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e969190612ca4565b91506040518060400160405280878381518110611eb557611eb5612cd3565b60200260200101516001600160a01b0316815260200183815250858281518110611ee157611ee1612cd3565b6020908102919091010152811561207f577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316868281518110611f2e57611f2e612cd3565b60200260200101516001600160a01b03160361204857604051632e1a7d4d60e01b8152600481018390527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690632e1a7d4d90602401600060405180830381600087803b158015611fa657600080fd5b505af1158015611fba573d6000803e3d6000fd5b5050604080516000808252602082019283905293503392508591611fde9190612f77565b60006040518083038185875af1925050503d806000811461201b576040519150601f19603f3d011682016040523d82523d6000602084013e612020565b606091505b50509050806120425760405163b12d13eb60e01b815260040160405180910390fd5b5061207f565b61207f338388848151811061205f5761205f612cd3565b60200260200101516001600160a01b0316611cfe9092919063ffffffff16565b8061208981612fa6565b915050611dd3565b50506120a082611cee90612c58565b5050919050565b6004546000906060906120c7906001600160a01b03878116911686611cfe565b600480546001600160a01b038881166000908152600760205260409081902054905163190a4d4960e01b8152919092169263190a4d4992612116928b928b928b928b9260ff9092169101612fbf565b6020604051808303816000875af1158015612135573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121599190612ca4565b6040805160018082528183019092529193506000919060208083019080368337019050509050858160008151811061219357612193612cd3565b60200260200101906001600160a01b031690816001600160a01b0316815250506121bc81611d56565b91505094509492505050565b6002600054036121eb57604051633ee5aeb560e01b815260040160405180910390fd5b6002600055565b60008061ffff8416156122425761271061221061ffff861685612ff3565b61221a919061300a565b6005549091506122359086906001600160a01b03168361269e565b61223f8184612ce9565b92505b829150935093915050565b6000836001600160a01b031663095ea7b38484604051602401612271929190612f5e565b604051602081830303815290604052915060e01b6020820180516001600160e01b03838183161783525050505090506122aa84826127f8565b6122df576122d584856001600160a01b031663095ea7b3866000604051602401611d24929190612f5e565b6122df8482612644565b50505050565b6001600160a01b038116158061236557506040516301ffc9a760e01b81526329f20dd560e11b60048201526001600160a01b038216906301ffc9a790602401602060405180830381865afa158015612341573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612365919061302c565b611a105760405162461bcd60e51b815260206004820152602260248201527f53706865726558206572726f723a206e6f7420612053706865726558456e67696044820152616e6560f01b60648201526084016109ba565b600063f45372f860006123d082600061186d565b6040516370a0823160e01b81529091506001600160a01b038616906370a08231906123ff9030906004016129e4565b602060405180830381865afa15801561241c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124409190612ca4565b9250611ce285858561269e565b60045460009060609061246d906001600160a01b03888116911686611cfe565b600480546001600160a01b038881166000908152600760205260409081902054905163616a3dbd60e11b8152919092169263c2d47b7a926124bc928b928a928a928d9260ff9092169101613049565b6020604051808303816000875af11580156124db573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124ff9190612ca4565b91506001600160a01b038316301461251d5761251b85846123bc565b505b6040805160018082528183019092526000916020808301908036833701905050905060008160008151811061219357612193612cd3565b805160609060008167ffffffffffffffff81111561257457612574612cbd565b60405190808252806020026020018201604052801561259d578160200160208202803683370190505b50905060005b828110156126035760008582815181106125bf576125bf612cd3565b60200260200101519050600081549050808484815181106125e2576125e2612cd3565b602002602001018181525050505080806125fb90612fa6565b9150506125a3565b509392505050565b6040516001600160a01b0384811660248301528381166044830152606482018390526122df9186918216906323b872dd90608401611d24565b60006126596001600160a01b038416836128a0565b9050805160001415801561267e57508080602001905181019061267c919061302c565b155b156106d55782604051635274afe760e01b81526004016109ba91906129e4565b80156106d5577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316836001600160a01b0316036127e457604051632e1a7d4d60e01b8152600481018290527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690632e1a7d4d90602401600060405180830381600087803b15801561273f57600080fd5b505af1158015612753573d6000803e3d6000fd5b5050604080516000808252602082019283905293506001600160a01b038616925084916127809190612f77565b60006040518083038185875af1925050503d80600081146127bd576040519150601f19603f3d011682016040523d82523d6000602084013e6127c2565b606091505b50509050806122df5760405163b12d13eb60e01b815260040160405180910390fd5b6106d56001600160a01b0384168383611cfe565b6000806000846001600160a01b0316846040516128159190612f77565b6000604051808303816000865af19150503d8060008114612852576040519150601f19603f3d011682016040523d82523d6000602084013e612857565b606091505b5091509150818015612881575080511580612881575080806020019051810190612881919061302c565b801561289757506000856001600160a01b03163b115b95945050505050565b60606128ae838360006128b5565b9392505050565b6060814710156128da573060405163cd78605960e01b81526004016109ba91906129e4565b600080856001600160a01b031684866040516128f69190612f77565b60006040518083038185875af1925050503d8060008114612933576040519150601f19603f3d011682016040523d82523d6000602084013e612938565b606091505b5091509150612948868383612952565b9695505050505050565b60608261296757612962826129a5565b6128ae565b815115801561297e57506001600160a01b0384163b155b1561299e5783604051639996b31560e01b81526004016109ba91906129e4565b50806128ae565b8051156129b55780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b634e487b7160e01b600052600160045260246000fd5b6001600160a01b0391909116815260200190565b6001600160a01b0381168114611a1057600080fd5b600060208284031215612a1f57600080fd5b81356128ae816129f8565b60008060008060808587031215612a4057600080fd5b8435612a4b816129f8565b93506020850135612a5b816129f8565b9250604085013591506060850135612a72816129f8565b939692955090935050565b600081518084526020808501945080840160005b83811015612ac157815180516001600160a01b031688528301518388015260409096019590820190600101612a91565b509495945050505050565b828152604060208201526000612ae56040830184612a7d565b949350505050565b60008060008060808587031215612b0357600080fd5b8435612b0e816129f8565b93506020850135612b1e816129f8565b93969395505050506040820135916060013590565b600060208284031215612b4557600080fd5b813561ffff811681146128ae57600080fd5b60008060008060808587031215612b6d57600080fd5b8435612b78816129f8565b9350602085013592506040850135612b8f816129f8565b9396929550929360600135925050565b634e487b7160e01b600052602160045260246000fd5b600a8110612bd357634e487b7160e01b600052602160045260246000fd5b9052565b602081016119e38284612bb5565b8015158114611a1057600080fd5b600080600060608486031215612c0857600080fd5b8335612c13816129f8565b92506020840135612c2381612be5565b91506040840135600a8110612c3757600080fd5b809150509250925092565b634e487b7160e01b600052601160045260246000fd5b6000600160ff1b8201612c6d57612c6d612c42565b5060000390565b6001600160a01b039586168152938516602085015260408401929092526060830152909116608082015260a00190565b600060208284031215612cb657600080fd5b5051919050565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b818103818111156119e3576119e3612c42565b6001600160a01b03939093168352602083019190915261ffff16604082015260600190565b600060208284031215612d3357600080fd5b81516128ae816129f8565b60018060a01b038716815285602082015284604082015283606082015282608082015260c060a08201526000612d7760c0830184612a7d565b98975050505050505050565b6001600160a01b0392831681529116602082015260400190565b6020808252601d908201527f53706865726558206572726f723a2061646d696e207265717569726564000000604082015260600190565b6001600160a01b0384168152821515602082015260608101612ae56040830184612bb5565b8481526001600160a01b03841660208201526060604082018190528101829052818360808301376000818301608090810191909152601f909201601f191601019392505050565b60006020808385031215612e5357600080fd5b825167ffffffffffffffff80821115612e6b57600080fd5b818501915085601f830112612e7f57600080fd5b815181811115612e9157612e91612cbd565b8060051b604051601f19603f83011681018181108582111715612eb657612eb6612cbd565b604052918252848201925083810185019188831115612ed457600080fd5b938501935b82851015612d7757845184529385019392850192612ed9565b600081518084526020808501945080840160005b83811015612ac157815187529582019590820190600101612f06565b848152836020820152608060408201526000612f416080830185612ef2565b8281036060840152612f538185612ef2565b979650505050505050565b6001600160a01b03929092168252602082015260400190565b6000825160005b81811015612f985760208186018101518583015201612f7e565b506000920191825250919050565b600060018201612fb857612fb8612c42565b5060010190565b6001600160a01b0386811682528581166020830152604082018590528316606082015260a081016129486080830184612bb5565b80820281158282048414176119e3576119e3612c42565b60008261302757634e487b7160e01b600052601260045260246000fd5b500490565b60006020828403121561303e57600080fd5b81516128ae81612be5565b6001600160a01b0386811682526020820186905284811660408301528316606082015260a081016129486080830184612bb556feadf90d75f0f2d657d5a22fa0f6e4dabb83c4598b77158eb4119cb38f1d8644633a517dd736309b905c692160a21b5439bdc56db1dc77d6982b66d44814e3fa97ca334bf49ef20e9cbff039feda3bc1c2a853aff17b1b5187e4aa1380ec55829d1777adabd324f814e2b0e28f6edf876dce01d7d66358c9acfe87b1b5f38338d6a2646970667358221220e0aee4aeb6db0c3f61c43ffb68da9dd25134753dde08db7a00f10990d2d97ec164736f6c6343000814003300000000000000000000000062137cec7754aa3f530c98866a86dba36d2687170000000000000000000000006969696969696969696969696969696969696969000000000000000000000000fcbd14dc51f0a4d49d5e53c2e0950e0bc26d0dce000000000000000000000000252e9b9dd86aad41166e682712b75a45bb779433000000000000000000000000944e034b1e3ff30becb3661b4960f47ddf14af3d0000000000000000000000001cfe31bfa1ac9b28588c91bb4300a1ed032f069f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

Deployed Bytecode

0x6080604052600436106101b25760003560e01c80639aa9019e116100ed578063cda5873e11610090578063cda5873e146104e3578063d55be8c614610503578063daba7ef714610519578063e74b981b14610539578063e7e5db4f14610559578063e9cbd8221461056e578063eb6d3a1114610596578063f99e6387146105ca578063fecc657c146105ea57600080fd5b80639aa9019e146103f6578063a863ff2f1461040b578063a89635bc1461042b578063ab033ea914610446578063afb44e3714610466578063bc063e1a1461026c578063c31c9c0714610486578063c64e74e2146104a657600080fd5b80634690484011610155578063469048401461030b5780634c6c848f1461032b5780634ee8f8581461034b5780635aa6e6751461036b5780638426b9681461038b5780638f0af424146103a05780639739c03e146103c057806399e80f3b146103e157600080fd5b80630c8106dc146101f657806315a292031461022c57806323af4e171461024c578063257d9bb81461026c5780633218e83a146102955780633c231166146102c35780633c3b1ef6146102d857806341273657146102eb57600080fd5b366101f157336001600160a01b037f000000000000000000000000696969696969696969696969696969696969696916146101ef576101ef6129ce565b005b600080fd5b34801561020257600080fd5b50600454610216906001600160a01b031681565b60405161022391906129e4565b60405180910390f35b34801561023857600080fd5b506101ef610247366004612a0d565b61062a565b34801561025857600080fd5b506101ef610267366004612a0d565b6106da565b34801561027857600080fd5b506102826103e881565b60405161ffff9091168152602001610223565b3480156102a157600080fd5b506102b56102b0366004612a2a565b61078d565b604051610223929190612acc565b3480156102cf57600080fd5b50610216610939565b6102b56102e6366004612aed565b610960565b3480156102f757600080fd5b506101ef610306366004612a0d565b610d13565b34801561031757600080fd5b50600554610216906001600160a01b031681565b34801561033757600080fd5b506101ef610346366004612a0d565b610db6565b34801561035757600080fd5b506101ef610366366004612a0d565b610eb9565b34801561037757600080fd5b50600254610216906001600160a01b031681565b34801561039757600080fd5b506101ef610f72565b3480156103ac57600080fd5b506101ef6103bb366004612b33565b61106b565b3480156103cc57600080fd5b506001546102829062010000900461ffff1681565b3480156103ed57600080fd5b5061021661111b565b34801561040257600080fd5b50610216611139565b34801561041757600080fd5b506102b5610426366004612b57565b611157565b34801561043757600080fd5b506001546102829061ffff1681565b34801561045257600080fd5b506101ef610461366004612a0d565b6113cf565b34801561047257600080fd5b506101ef610481366004612b33565b61144f565b34801561049257600080fd5b50600354610216906001600160a01b031681565b3480156104b257600080fd5b506104d66104c1366004612a0d565b60076020526000908152604090205460ff1681565b6040516102239190612bd7565b3480156104ef57600080fd5b506101ef6104fe366004612bf3565b61150b565b34801561050f57600080fd5b5061028261271081565b34801561052557600080fd5b506101ef610534366004612a0d565b6115cf565b34801561054557600080fd5b506101ef610554366004612a0d565b611674565b34801561056557600080fd5b506102166116fa565b34801561057a57600080fd5b506001546102169064010000000090046001600160a01b031681565b3480156105a257600080fd5b506102167f000000000000000000000000696969696969696969696969696969696969696981565b3480156105d657600080fd5b506102b56105e5366004612a2a565b611718565b3480156105f657600080fd5b5061061a610605366004612a0d565b60066020526000908152604090205460ff1681565b6040519015158152602001610223565b6002546001600160a01b0316331461065557604051632d5be4cb60e21b815260040160405180910390fd5b6326b2eef4600061066782600161186d565b9050610672836119e9565b600480546001600160a01b038581166001600160a01b0319831681179093556040519116919082907f82c52d8aa62200dfc997bdf1100b1c2ed614580941b2b8637d10b8ca0428eeec90600090a3506106d56106cd83612c58565b600183611a13565b505050565b6002546001600160a01b0316331461070557604051632d5be4cb60e21b815260040160405180910390fd5b635b9fdae4600061071782600161186d565b9050610722836119e9565b60018054640100000000600160c01b0319166401000000006001600160a01b03868116828102939093179384905560405192939190910416907f497b8419a37c8014b1a755ec707f25d11ae4bade591d379f9a4095f37edaf76e90600090a36106d56106cd83612c58565b600060606307c1e85d63190c741d60e11b836107b68382356001600160e01b031916841461186d565b90506001600160a01b03861630146107d5576107d28888611b35565b96505b6001600160a01b03891660009081526006602052604090205460609060ff16156108f657600354610813906001600160a01b038b811691168a611cfe565b6003546040516305c13d4360e11b81526001600160a01b0390911690630b827a869061084c908c908e908d906000908e90600401612c74565b6020604051808303816000875af115801561086b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061088f9190612ca4565b6040805160018082528183019092529197506020808301908036833701905050905088816000815181106108c5576108c5612cd3565b60200260200101906001600160a01b031690816001600160a01b0316815250506108ee81611d56565b945050610908565b6109028a8a8a8a6120a7565b95509550505b61092d61091484612c58565b6000356001600160e01b03199081169085161483611a13565b50505094509492505050565b600061095b61095760016000805160206130de833981519152612ce9565b5490565b905090565b6000606061096c6121c8565b63621b094a600061097e82600161186d565b90506001600160a01b038716610a62576103e83410156109c3576000346103e8604051630b722aef60e21b81526004016109ba93929190612cfc565b60405180910390fd5b7f00000000000000000000000069696969696969696969696969696969696969696001600160a01b031663d0e30db0346040518263ffffffff1660e01b81526004016000604051808303818588803b158015610a1e57600080fd5b505af1158015610a32573d6000803e3d6000fd5b50505050507f00000000000000000000000069696969696969696969696969696969696969699650349550610a9a565b6103e8861015610a8d5786866103e8604051630b722aef60e21b81526004016109ba93929190612cfc565b610a978787611b35565b95505b600154600090610ab090899061ffff16896121f2565b80925081985050506000879050886001600160a01b03168a6001600160a01b03166338d52e0f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610b05573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b299190612d21565b6001600160a01b031614610ba957610ba48a6001600160a01b03166338d52e0f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610b78573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b9c9190612d21565b8a8a3061078d565b955090505b610c208a828c6001600160a01b03166338d52e0f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610bec573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c109190612d21565b6001600160a01b0316919061224d565b60405163bc157ac160e01b815260048101829052336024820152604481018890526001600160a01b038b169063bc157ac1906064016020604051808303816000875af1158015610c74573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c989190612ca4565b9550896001600160a01b0316336001600160a01b03167f87640c2e40d67703e350e7d24d4e416e5c4f1e1550e1285b1bf667440d36bdcf8b8b858b888c604051610ce796959493929190612d3e565b60405180910390a35050610cfe826106cd90612c58565b5050610d0a6001600055565b94509492505050565b6002546001600160a01b03163314610d3e57604051632d5be4cb60e21b815260040160405180910390fd5b63d473ef3c6000610d5082600161186d565b9050610d5b836119e9565b600380546001600160a01b038581166001600160a01b0319831681179093556040519116919082907f5c5b0bb5085481c320dc523e3b3f829eaa807936f615add0077fa213f345090e90600090a3506106d56106cd83612c58565b610dd2610957600160008051602061307e833981519152612ce9565b6001600160a01b0316336001600160a01b031614610e325760405162461bcd60e51b815260206004820181905260248201527f53706865726558206572726f723a206f70657261746f7220726571756972656460448201526064016109ba565b610e3b816122e5565b6000610e5961095760016000805160206130de833981519152612ce9565b9050610e7c610e7760016000805160206130de833981519152612ce9565b839055565b7ff33499cccaa0611882086224cc48cd82ef54b66a4d2edf4ed67108dd516896d58183604051610ead929190612d83565b60405180910390a15050565b610ed561095760016000805160206130be833981519152612ce9565b6001600160a01b0316336001600160a01b031614610f055760405162461bcd60e51b81526004016109ba90612d9d565b6000610f23610957600160008051602061307e833981519152612ce9565b9050610f41610e77600160008051602061307e833981519152612ce9565b7f2ac55ae7ba47db34b5334622acafeb34a65daf143b47019273185d64c73a35a58183604051610ead929190612d83565b33610f7b611139565b6001600160a01b031614610fe05760405162461bcd60e51b815260206004820152602660248201527f53706865726558206572726f723a206e6f74207468652070656e64696e67206160448201526518d8dbdd5b9d60d21b60648201526084016109ba565b6000610fea61111b565b905061100d61100860016000805160206130be833981519152612ce9565b339055565b61102f611029600160008051602061309e833981519152612ce9565b60009055565b7f67ebaebcd2ca5a91a404e898110f221747e8d15567f2388a34794aab151cf3e68133604051611060929190612d83565b60405180910390a150565b6002546001600160a01b0316331461109657604051632d5be4cb60e21b815260040160405180910390fd5b6103e861ffff821611156110cb57604051633c057d7b60e01b815261ffff821660048201526103e860248201526044016109ba565b6001805461ffff83811661ffff1983168117909355604080519190921680825260208201939093527fcd5c26e400cb366bdac1d1cbdec4b428f15bc14a133ecbda85bc6f11f7acb9f19101610ead565b600061095b61095760016000805160206130be833981519152612ce9565b600061095b610957600160008051602061309e833981519152612ce9565b600060606111636121c8565b63af73b205600061117582600161186d565b604051635d043b2960e11b8152600481018990523060248201523360448201529091506000906001600160a01b038a169063ba087652906064016020604051808303816000875af11580156111ce573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111f29190612ca4565b90506001600160a01b038716611226577f000000000000000000000000696969696969696969696969696969696969696996505b866001600160a01b0316896001600160a01b03166338d52e0f6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561126e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112929190612d21565b6001600160a01b0316036112a85780945061131b565b611315896001600160a01b03166338d52e0f6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156112e9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061130d9190612d21565b888330611718565b90955093505b600061133888600160029054906101000a900461ffff16886121f2565b909650905061134788336123bc565b508686101561138257604051632752fb4960e11b81526001600160a01b038916600482015260248101879052604481018890526064016109ba565b896001600160a01b0316336001600160a01b03167f0bc6c5ccdba0b566b3adbb7c24c419a1b872218cd487d145a4e4eb70b2c8bfb38a89868e878c604051610ce796959493929190612d3e565b6002546001600160a01b031633146113fa57604051632d5be4cb60e21b815260040160405180910390fd5b611403816119e9565b600280546001600160a01b0319166001600160a01b03831690811790915560405181907f3aaaebeb4821d6a7e5c77ece53cff0afcc56c82add2c978dbbb7f73e84cbcfd290600090a350565b6002546001600160a01b0316331461147a57604051632d5be4cb60e21b815260040160405180910390fd5b6103e861ffff821611156114af57604051633c057d7b60e01b815261ffff821660048201526103e860248201526044016109ba565b6001805461ffff8381166201000081810263ffff00001985161790945560408051949093049091168084526020840191909152917febcac71ae552a15ae4d23c933603e1a71f47ff8371bdd98194f037156b73a7199101610ead565b6002546001600160a01b0316331461153657604051632d5be4cb60e21b815260040160405180910390fd5b61153f836119e9565b6001600160a01b0383166000908152600660209081526040808320805486151560ff199182161790915560079092529091208054839216600183600981111561158a5761158a612b9f565b02179055507f3b79c840859aa9e93d93ccb4f7a8b6c425621929bcc61a03194f607a53d0f4ab8383836040516115c293929190612dd4565b60405180910390a1505050565b6115eb61095760016000805160206130be833981519152612ce9565b6001600160a01b0316336001600160a01b03161461161b5760405162461bcd60e51b81526004016109ba90612d9d565b61163c611637600160008051602061309e833981519152612ce9565b829055565b7f5778f1547abbbb86090a43c32aec38334b31df4beeb6f8f3fa063f593b53a52661166561111b565b82604051611060929190612d83565b6002546001600160a01b0316331461169f57604051632d5be4cb60e21b815260040160405180910390fd5b6116a8816119e9565b600580546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f0bc21fe5c3ab742ff1d15b5c4477ffbacf1167e618228078fa625edebe7f331d90600090a35050565b600061095b610957600160008051602061307e833981519152612ce9565b6000606063362279f763f99e638760e01b836117418382356001600160e01b031916841461186d565b90506001600160a01b03861630146117605761175d8988611b35565b96505b6001600160a01b03891660009081526006602052604090205460ff16156118515760035461179b906001600160a01b038b8116911689611cfe565b6003546040516305c13d4360e11b81526001600160a01b0390911690630b827a86906117d4908c908c908c906000908d90600401612c74565b6020604051808303816000875af11580156117f3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118179190612ca4565b604080516001808252818301909252919650600091906020808301908036833701905050905089816000815181106108c5576108c5612cd3565b61185d8989898961244d565b9450945061092d61091484612c58565b6118a1604051806080016040528060608152602001606081526020016000815260200160006001600160a01b031681525090565b60006118ab610939565b6001600160a01b0316146119e35760006118c3610939565b9050821561194c57604051634492e52d60e11b81526001600160a01b03821690638925ca5a906118fe90879033906000903690600401612df9565b6000604051808303816000875af115801561191d573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526119459190810190612e40565b82526119be565b604051633e88494360e01b8152600481018590526001600160a01b03821690633e884943906024016000604051808303816000875af1158015611993573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526119bb9190810190612e40565b82525b81516119c990612554565b60208301525a60408301526001600160a01b031660608201525b92915050565b6001600160a01b038116611a105760405163d92e233d60e01b815260040160405180910390fd5b50565b60608101516001600160a01b038116611a2c5750505050565b60005a8360400151611a3e9190612ce9565b90506060611a4f8460000151612554565b90508415611ac4576020840151604051631e17b28d60e31b81526001600160a01b0385169163f0bd946891611a8d918a918791908790600401612f22565b600060405180830381600087803b158015611aa757600080fd5b505af1158015611abb573d6000803e3d6000fd5b50505050611b2d565b602084015160405163027313cd60e11b81526001600160a01b038516916304e6279a91611afa918a918791908790600401612f22565b600060405180830381600087803b158015611b1457600080fd5b505af1158015611b28573d6000803e3d6000fd5b505050505b505050505050565b6000639ffc46616000611b4982600061186d565b905083856001600160a01b031663dd62ed3e33306040518363ffffffff1660e01b8152600401611b7a929190612d83565b602060405180830381865afa158015611b97573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bbb9190612ca4565b1015611bda576040516332da96a360e01b815260040160405180910390fd5b6040516370a0823160e01b81526000906001600160a01b038716906370a0823190611c099030906004016129e4565b602060405180830381865afa158015611c26573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c4a9190612ca4565b9050611c616001600160a01b03871633308861260b565b6040516370a0823160e01b81526000906001600160a01b038816906370a0823190611c909030906004016129e4565b602060405180830381865afa158015611cad573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cd19190612ca4565b9050611cdd8282612ce9565b945050505b611cf6611cee83612c58565b600083611a13565b505092915050565b6106d583846001600160a01b031663a9059cbb8585604051602401611d24929190612f5e565b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050612644565b60606365afacc16000611d6a82600061186d565b90506000845167ffffffffffffffff811115611d8857611d88612cbd565b604051908082528060200260200182016040528015611dcd57816020015b6040805180820190915260008082526020820152815260200190600190039081611da65790505b50935060005b85518110156120915760006001600160a01b0316868281518110611df957611df9612cd3565b60200260200101516001600160a01b0316031561207f57858181518110611e2257611e22612cd3565b60200260200101516001600160a01b03166370a08231306040518263ffffffff1660e01b8152600401611e5591906129e4565b602060405180830381865afa158015611e72573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e969190612ca4565b91506040518060400160405280878381518110611eb557611eb5612cd3565b60200260200101516001600160a01b0316815260200183815250858281518110611ee157611ee1612cd3565b6020908102919091010152811561207f577f00000000000000000000000069696969696969696969696969696969696969696001600160a01b0316868281518110611f2e57611f2e612cd3565b60200260200101516001600160a01b03160361204857604051632e1a7d4d60e01b8152600481018390527f00000000000000000000000069696969696969696969696969696969696969696001600160a01b031690632e1a7d4d90602401600060405180830381600087803b158015611fa657600080fd5b505af1158015611fba573d6000803e3d6000fd5b5050604080516000808252602082019283905293503392508591611fde9190612f77565b60006040518083038185875af1925050503d806000811461201b576040519150601f19603f3d011682016040523d82523d6000602084013e612020565b606091505b50509050806120425760405163b12d13eb60e01b815260040160405180910390fd5b5061207f565b61207f338388848151811061205f5761205f612cd3565b60200260200101516001600160a01b0316611cfe9092919063ffffffff16565b8061208981612fa6565b915050611dd3565b50506120a082611cee90612c58565b5050919050565b6004546000906060906120c7906001600160a01b03878116911686611cfe565b600480546001600160a01b038881166000908152600760205260409081902054905163190a4d4960e01b8152919092169263190a4d4992612116928b928b928b928b9260ff9092169101612fbf565b6020604051808303816000875af1158015612135573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121599190612ca4565b6040805160018082528183019092529193506000919060208083019080368337019050509050858160008151811061219357612193612cd3565b60200260200101906001600160a01b031690816001600160a01b0316815250506121bc81611d56565b91505094509492505050565b6002600054036121eb57604051633ee5aeb560e01b815260040160405180910390fd5b6002600055565b60008061ffff8416156122425761271061221061ffff861685612ff3565b61221a919061300a565b6005549091506122359086906001600160a01b03168361269e565b61223f8184612ce9565b92505b829150935093915050565b6000836001600160a01b031663095ea7b38484604051602401612271929190612f5e565b604051602081830303815290604052915060e01b6020820180516001600160e01b03838183161783525050505090506122aa84826127f8565b6122df576122d584856001600160a01b031663095ea7b3866000604051602401611d24929190612f5e565b6122df8482612644565b50505050565b6001600160a01b038116158061236557506040516301ffc9a760e01b81526329f20dd560e11b60048201526001600160a01b038216906301ffc9a790602401602060405180830381865afa158015612341573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612365919061302c565b611a105760405162461bcd60e51b815260206004820152602260248201527f53706865726558206572726f723a206e6f7420612053706865726558456e67696044820152616e6560f01b60648201526084016109ba565b600063f45372f860006123d082600061186d565b6040516370a0823160e01b81529091506001600160a01b038616906370a08231906123ff9030906004016129e4565b602060405180830381865afa15801561241c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124409190612ca4565b9250611ce285858561269e565b60045460009060609061246d906001600160a01b03888116911686611cfe565b600480546001600160a01b038881166000908152600760205260409081902054905163616a3dbd60e11b8152919092169263c2d47b7a926124bc928b928a928a928d9260ff9092169101613049565b6020604051808303816000875af11580156124db573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124ff9190612ca4565b91506001600160a01b038316301461251d5761251b85846123bc565b505b6040805160018082528183019092526000916020808301908036833701905050905060008160008151811061219357612193612cd3565b805160609060008167ffffffffffffffff81111561257457612574612cbd565b60405190808252806020026020018201604052801561259d578160200160208202803683370190505b50905060005b828110156126035760008582815181106125bf576125bf612cd3565b60200260200101519050600081549050808484815181106125e2576125e2612cd3565b602002602001018181525050505080806125fb90612fa6565b9150506125a3565b509392505050565b6040516001600160a01b0384811660248301528381166044830152606482018390526122df9186918216906323b872dd90608401611d24565b60006126596001600160a01b038416836128a0565b9050805160001415801561267e57508080602001905181019061267c919061302c565b155b156106d55782604051635274afe760e01b81526004016109ba91906129e4565b80156106d5577f00000000000000000000000069696969696969696969696969696969696969696001600160a01b0316836001600160a01b0316036127e457604051632e1a7d4d60e01b8152600481018290527f00000000000000000000000069696969696969696969696969696969696969696001600160a01b031690632e1a7d4d90602401600060405180830381600087803b15801561273f57600080fd5b505af1158015612753573d6000803e3d6000fd5b5050604080516000808252602082019283905293506001600160a01b038616925084916127809190612f77565b60006040518083038185875af1925050503d80600081146127bd576040519150601f19603f3d011682016040523d82523d6000602084013e6127c2565b606091505b50509050806122df5760405163b12d13eb60e01b815260040160405180910390fd5b6106d56001600160a01b0384168383611cfe565b6000806000846001600160a01b0316846040516128159190612f77565b6000604051808303816000865af19150503d8060008114612852576040519150601f19603f3d011682016040523d82523d6000602084013e612857565b606091505b5091509150818015612881575080511580612881575080806020019051810190612881919061302c565b801561289757506000856001600160a01b03163b115b95945050505050565b60606128ae838360006128b5565b9392505050565b6060814710156128da573060405163cd78605960e01b81526004016109ba91906129e4565b600080856001600160a01b031684866040516128f69190612f77565b60006040518083038185875af1925050503d8060008114612933576040519150601f19603f3d011682016040523d82523d6000602084013e612938565b606091505b5091509150612948868383612952565b9695505050505050565b60608261296757612962826129a5565b6128ae565b815115801561297e57506001600160a01b0384163b155b1561299e5783604051639996b31560e01b81526004016109ba91906129e4565b50806128ae565b8051156129b55780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b634e487b7160e01b600052600160045260246000fd5b6001600160a01b0391909116815260200190565b6001600160a01b0381168114611a1057600080fd5b600060208284031215612a1f57600080fd5b81356128ae816129f8565b60008060008060808587031215612a4057600080fd5b8435612a4b816129f8565b93506020850135612a5b816129f8565b9250604085013591506060850135612a72816129f8565b939692955090935050565b600081518084526020808501945080840160005b83811015612ac157815180516001600160a01b031688528301518388015260409096019590820190600101612a91565b509495945050505050565b828152604060208201526000612ae56040830184612a7d565b949350505050565b60008060008060808587031215612b0357600080fd5b8435612b0e816129f8565b93506020850135612b1e816129f8565b93969395505050506040820135916060013590565b600060208284031215612b4557600080fd5b813561ffff811681146128ae57600080fd5b60008060008060808587031215612b6d57600080fd5b8435612b78816129f8565b9350602085013592506040850135612b8f816129f8565b9396929550929360600135925050565b634e487b7160e01b600052602160045260246000fd5b600a8110612bd357634e487b7160e01b600052602160045260246000fd5b9052565b602081016119e38284612bb5565b8015158114611a1057600080fd5b600080600060608486031215612c0857600080fd5b8335612c13816129f8565b92506020840135612c2381612be5565b91506040840135600a8110612c3757600080fd5b809150509250925092565b634e487b7160e01b600052601160045260246000fd5b6000600160ff1b8201612c6d57612c6d612c42565b5060000390565b6001600160a01b039586168152938516602085015260408401929092526060830152909116608082015260a00190565b600060208284031215612cb657600080fd5b5051919050565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b818103818111156119e3576119e3612c42565b6001600160a01b03939093168352602083019190915261ffff16604082015260600190565b600060208284031215612d3357600080fd5b81516128ae816129f8565b60018060a01b038716815285602082015284604082015283606082015282608082015260c060a08201526000612d7760c0830184612a7d565b98975050505050505050565b6001600160a01b0392831681529116602082015260400190565b6020808252601d908201527f53706865726558206572726f723a2061646d696e207265717569726564000000604082015260600190565b6001600160a01b0384168152821515602082015260608101612ae56040830184612bb5565b8481526001600160a01b03841660208201526060604082018190528101829052818360808301376000818301608090810191909152601f909201601f191601019392505050565b60006020808385031215612e5357600080fd5b825167ffffffffffffffff80821115612e6b57600080fd5b818501915085601f830112612e7f57600080fd5b815181811115612e9157612e91612cbd565b8060051b604051601f19603f83011681018181108582111715612eb657612eb6612cbd565b604052918252848201925083810185019188831115612ed457600080fd5b938501935b82851015612d7757845184529385019392850192612ed9565b600081518084526020808501945080840160005b83811015612ac157815187529582019590820190600101612f06565b848152836020820152608060408201526000612f416080830185612ef2565b8281036060840152612f538185612ef2565b979650505050505050565b6001600160a01b03929092168252602082015260400190565b6000825160005b81811015612f985760208186018101518583015201612f7e565b506000920191825250919050565b600060018201612fb857612fb8612c42565b5060010190565b6001600160a01b0386811682528581166020830152604082018590528316606082015260a081016129486080830184612bb5565b80820281158282048414176119e3576119e3612c42565b60008261302757634e487b7160e01b600052601260045260246000fd5b500490565b60006020828403121561303e57600080fd5b81516128ae81612be5565b6001600160a01b0386811682526020820186905284811660408301528316606082015260a081016129486080830184612bb556feadf90d75f0f2d657d5a22fa0f6e4dabb83c4598b77158eb4119cb38f1d8644633a517dd736309b905c692160a21b5439bdc56db1dc77d6982b66d44814e3fa97ca334bf49ef20e9cbff039feda3bc1c2a853aff17b1b5187e4aa1380ec55829d1777adabd324f814e2b0e28f6edf876dce01d7d66358c9acfe87b1b5f38338d6a2646970667358221220e0aee4aeb6db0c3f61c43ffb68da9dd25134753dde08db7a00f10990d2d97ec164736f6c63430008140033

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

00000000000000000000000062137cec7754aa3f530c98866a86dba36d2687170000000000000000000000006969696969696969696969696969696969696969000000000000000000000000fcbd14dc51f0a4d49d5e53c2e0950e0bc26d0dce000000000000000000000000252e9b9dd86aad41166e682712b75a45bb779433000000000000000000000000944e034b1e3ff30becb3661b4960f47ddf14af3d0000000000000000000000001cfe31bfa1ac9b28588c91bb4300a1ed032f069f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

-----Decoded View---------------
Arg [0] : devAddress (address): 0x62137cEc7754aA3f530c98866a86dba36d268717
Arg [1] : wrappedNative (address): 0x6969696969696969696969696969696969696969
Arg [2] : stablecoin (address): 0xFCBD14DC51f0A4d49d5E53C2E0950e0bC26d0Dce
Arg [3] : swapRouter (address): 0x252E9B9Dd86AAD41166e682712B75A45Bb779433
Arg [4] : lpRouter (address): 0x944e034B1E3fF30BeCb3661b4960f47DdF14Af3D
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] : 000000000000000000000000252e9b9dd86aad41166e682712b75a45bb779433
Arg [4] : 000000000000000000000000944e034b1e3ff30becb3661b4960f47ddf14af3d
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.