POL Price: $0.217961 (-0.17%)
Gas: 30 GWei
 

Overview

POL Balance

Polygon PoS Chain LogoPolygon PoS Chain LogoPolygon PoS Chain Logo0 POL

POL Value

$0.00

Token Holdings

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
X Bundle534386752024-02-12 16:33:41435 days ago1707755621IN
0x1B8cfd1E...290Ad8AA8
0 POL0.13148179244.15804737
X Bundle534386352024-02-12 16:32:15435 days ago1707755535IN
0x1B8cfd1E...290Ad8AA8
0 POL0.07582978242.05113825
X Bundle510132772023-12-11 16:23:33498 days ago1702311813IN
0x1B8cfd1E...290Ad8AA8
0 POL0.843949951,156.41265348
X Bundle509293922023-12-09 12:01:42500 days ago1702123302IN
0x1B8cfd1E...290Ad8AA8
0 POL0.07337161115
X Bundle509072172023-12-08 22:38:24501 days ago1702075104IN
0x1B8cfd1E...290Ad8AA8
0 POL0.0497173587.71911271
X Bundle504091432023-11-26 10:41:52513 days ago1700995312IN
0x1B8cfd1E...290Ad8AA8
0 POL0.23534487327.73364993
X Bundle491721702023-10-26 11:10:18544 days ago1698318618IN
0x1B8cfd1E...290Ad8AA8
0 POL0.15755154219.36818189
X Bundle483942412023-10-06 11:41:03564 days ago1696592463IN
0x1B8cfd1E...290Ad8AA8
0 POL0.08121715121.78419984
X Bundle482411602023-10-02 14:31:29568 days ago1696257089IN
0x1B8cfd1E...290Ad8AA8
0 POL0.0819574114.21456592
X Bundle482403742023-10-02 14:01:31568 days ago1696255291IN
0x1B8cfd1E...290Ad8AA8
0 POL0.0415191378.20283543
X Bundle477426112023-09-19 19:58:07581 days ago1695153487IN
0x1B8cfd1E...290Ad8AA8
0 POL0.09631456133.38267417
X Bundle470583542023-09-02 14:20:34598 days ago1693664434IN
0x1B8cfd1E...290Ad8AA8
0 POL0.0634818898.03607532
Bump Transfer466309642023-08-22 20:57:40609 days ago1692737860IN
0x1B8cfd1E...290Ad8AA8
0.61387128 POL0.00672127109.65456072
X Bundle466309532023-08-22 20:57:16609 days ago1692737836IN
0x1B8cfd1E...290Ad8AA8
0 POL0.0315990477.50621036
X Bundle464540672023-08-18 11:09:23613 days ago1692356963IN
0x1B8cfd1E...290Ad8AA8
0 POL0.08505004132.53677543
Bump Transfer459595952023-08-06 0:55:11626 days ago1691283311IN
0x1B8cfd1E...290Ad8AA8
0.55920012 POL0.0060053497.97452923
X Bundle459595842023-08-06 0:54:49626 days ago1691283289IN
0x1B8cfd1E...290Ad8AA8
0 POL0.0256086766.54817892
X Bundle455471092023-07-26 16:55:26636 days ago1690390526IN
0x1B8cfd1E...290Ad8AA8
0 POL0.0392532879.41604628
X Bundle455033882023-07-25 13:52:41637 days ago1690293161IN
0x1B8cfd1E...290Ad8AA8
0 POL0.0372277385.6605588
X Bundle455032952023-07-25 13:49:23637 days ago1690292963IN
0x1B8cfd1E...290Ad8AA8
0 POL0.02958167108.27606726
X Bundle455026692023-07-25 13:24:31637 days ago1690291471IN
0x1B8cfd1E...290Ad8AA8
0 POL0.07804884159.10575076
Bump Transfer453429702023-07-21 12:16:10641 days ago1689941770IN
0x1B8cfd1E...290Ad8AA8
1.30091185 POL0.00819358133.67467122
X Bundle453429582023-07-21 12:15:44641 days ago1689941744IN
0x1B8cfd1E...290Ad8AA8
0 POL0.03990187105.13940153
Bump Transfer452415882023-07-18 22:31:01644 days ago1689719461IN
0x1B8cfd1E...290Ad8AA8
1.70743291 POL0.01743914284.51178442
X Bundle452415792023-07-18 22:30:33644 days ago1689719433IN
0x1B8cfd1E...290Ad8AA8
0 POL0.09183221238.60456544
View all transactions

Latest 25 internal transactions (View All)

Parent Transaction Hash Block From To
466309642023-08-22 20:57:40609 days ago1692737860
0x1B8cfd1E...290Ad8AA8
0.61387128 POL
459595952023-08-06 0:55:11626 days ago1691283311
0x1B8cfd1E...290Ad8AA8
0.55920012 POL
453429702023-07-21 12:16:10641 days ago1689941770
0x1B8cfd1E...290Ad8AA8
1.30091185 POL
452415882023-07-18 22:31:01644 days ago1689719461
0x1B8cfd1E...290Ad8AA8
1.70743291 POL
452338322023-07-18 17:10:37644 days ago1689700237
0x1B8cfd1E...290Ad8AA8
0.51888873 POL
452313402023-07-18 15:27:02644 days ago1689694022
0x1B8cfd1E...290Ad8AA8
0.00122499 POL
451822152023-07-17 9:40:51645 days ago1689586851
0x1B8cfd1E...290Ad8AA8
0.50619651 POL
451487522023-07-16 13:30:20646 days ago1689514220
0x1B8cfd1E...290Ad8AA8
0.49878379 POL
451473692023-07-16 12:40:48646 days ago1689511248
0x1B8cfd1E...290Ad8AA8
0.49754281 POL
451388852023-07-16 7:31:47647 days ago1689492707
0x1B8cfd1E...290Ad8AA8
0.49582023 POL
450735002023-07-14 15:40:08648 days ago1689349208
0x1B8cfd1E...290Ad8AA8
2.67282297 POL
450719972023-07-14 14:42:34648 days ago1689345754
0x1B8cfd1E...290Ad8AA8
0.00115637 POL
450666292023-07-14 11:21:40648 days ago1689333700
0x1B8cfd1E...290Ad8AA8
0.47862928 POL
450614742023-07-14 8:15:25648 days ago1689322525
0x1B8cfd1E...290Ad8AA8
1.11646819 POL
450325412023-07-13 14:45:14649 days ago1689259514
0x1B8cfd1E...290Ad8AA8
0.00123724 POL
450249932023-07-13 10:08:27649 days ago1689242907
0x1B8cfd1E...290Ad8AA8
0.50468743 POL
450218862023-07-13 8:12:30649 days ago1689235950
0x1B8cfd1E...290Ad8AA8
0.52458859 POL
450115512023-07-13 1:52:34650 days ago1689213154
0x1B8cfd1E...290Ad8AA8
0.53170531 POL
450069302023-07-12 23:03:09650 days ago1689202989
0x1B8cfd1E...290Ad8AA8
1.53108119 POL
449974402023-07-12 17:24:28650 days ago1689182668
0x1B8cfd1E...290Ad8AA8
5 POL
449962302023-07-12 16:41:01650 days ago1689180061
0x1B8cfd1E...290Ad8AA8
0.52617033 POL
449629842023-07-11 20:25:50651 days ago1689107150
0x1B8cfd1E...290Ad8AA8
1.71861532 POL
449628992023-07-11 20:22:50651 days ago1689106970
0x1B8cfd1E...290Ad8AA8
1.52427771 POL
449626592023-07-11 20:14:20651 days ago1689106460
0x1B8cfd1E...290Ad8AA8
0.5162461 POL
449606552023-07-11 19:03:02651 days ago1689102182
0x1B8cfd1E...290Ad8AA8
2.02522431 POL
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
ConnextRouter

Compiler Version
v0.8.15+commit.e14f2714

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion
File 1 of 25 : ConnextRouter.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.15;

/**
 * @title ConnextRouter
 *
 * @author Fujidao Labs
 *
 * @notice A Router implementing Connext specific bridging logic.
 */

import {SafeERC20} from "openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol";
import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
import {IConnext, IXReceiver} from "../interfaces/connext/IConnext.sol";
import {ConnextHandler} from "./ConnextHandler.sol";
import {BaseRouter} from "../abstracts/BaseRouter.sol";
import {IWETH9} from "../abstracts/WETH9.sol";
import {IVault} from "../interfaces/IVault.sol";
import {IVaultPermissions} from "../interfaces/IVaultPermissions.sol";
import {IChief} from "../interfaces/IChief.sol";
import {IRouter} from "../interfaces/IRouter.sol";
import {IFlasher} from "../interfaces/IFlasher.sol";
import {LibBytes} from "../libraries/LibBytes.sol";

contract ConnextRouter is BaseRouter, IXReceiver {
  /**
   * @dev Emitted when a new destination router gets added.
   *
   * @param router the router on another chain
   * @param domain the destination domain identifier according Connext nomenclature
   */
  event NewRouterAdded(address indexed router, uint256 indexed domain);

  /**
   * @dev Emitted when Connext `xCall` is invoked.
   *
   * @param transferId the unique identifier of the crosschain transfer
   * @param caller the account that called the function
   * @param receiver the router on destDomain
   * @param destDomain the destination domain identifier according Connext nomenclature
   * @param asset the asset being transferred
   * @param amount the amount of transferring asset the recipient address receives
   * @param callData the calldata sent to destination router that will get decoded and executed
   */
  event XCalled(
    bytes32 indexed transferId,
    address indexed caller,
    address indexed receiver,
    uint256 destDomain,
    address asset,
    uint256 amount,
    bytes callData
  );

  /**
   * @dev Emitted when the router receives a cross-chain call.
   *
   * @param transferId the unique identifier of the crosschain transfer
   * @param originDomain the origin domain identifier according Connext nomenclature
   * @param success whether or not the xBundle call succeeds
   * @param asset the asset being transferred
   * @param amount the amount of transferring asset the recipient address receives
   * @param callData the calldata that will get decoded and executed
   */
  event XReceived(
    bytes32 indexed transferId,
    uint256 indexed originDomain,
    bool success,
    address asset,
    uint256 amount,
    bytes callData
  );

  /// @dev Custom Errors
  error ConnextRouter__setRouter_invalidInput();
  error ConnextRouter__xReceive_notReceivedAssetBalance();
  error ConnextRouter__xReceive_notAllowedCaller();
  error ConnextRouter__xReceiver_noValueTransferUseXbundle();
  error ConnnextRouter__xBundleConnext_notSelfCalled();

  /// @dev The connext contract on the origin domain.
  IConnext public immutable connext;

  ConnextHandler public immutable handler;

  /**
   * @notice A mapping of a domain of another chain and a deployed router there.
   *
   * @dev For the list of domains supported by Connext,
   * plz check: https://docs.connext.network/resources/deployments
   */
  mapping(uint256 => address) public routerByDomain;

  modifier onlySelf() {
    if (msg.sender != address(this)) {
      revert ConnnextRouter__xBundleConnext_notSelfCalled();
    }
    _;
  }

  constructor(IWETH9 weth, IConnext connext_, IChief chief) BaseRouter(weth, chief) {
    connext = connext_;
    handler = new ConnextHandler(address(this));
    _allowCaller(msg.sender, true);
  }

  /*////////////////////////////////////
        Connext specific functions
  ////////////////////////////////////*/

  /**
   * @notice Called by Connext on the destination chain.
   *
   * @param transferId the unique identifier of the crosschain transfer
   * @param amount the amount of transferring asset, after slippage, the recipient address receives
   * @param asset the asset being transferred
   * @param originSender the address of the contract or EOA that called xcall on the origin chain
   * @param originDomain the origin domain identifier according Connext nomenclature
   * @param callData the calldata that will get decoded and executed, see "Requirements"
   *
   * @dev It does not perform authentication of the calling address. As a result of that,
   * all txns go through Connext's fast path.
   * If `xBundle` fails internally, this contract will send the received funds to {ConnextHandler}.
   *
   * Requirements:
   * - `calldata` parameter must be encoded with the following structure:
   *     > abi.encode(Action[] actions, bytes[] args)
   * - actions: array of serialized actions to execute from available enum {IRouter.Action}.
   * - args: array of encoded arguments according to each action. See {BaseRouter-internalBundle}.
   */
  function xReceive(
    bytes32 transferId,
    uint256 amount,
    address asset,
    address originSender,
    uint32 originDomain,
    bytes memory callData
  )
    external
    returns (bytes memory)
  {
    (Action[] memory actions, bytes[] memory args) = abi.decode(callData, (Action[], bytes[]));

    uint256 balance;
    uint256 beforeSlipped;
    if (amount > 0) {
      // Ensure that at this entry point expected `asset` `amount` is received.
      balance = IERC20(asset).balanceOf(address(this));
      if (balance < amount) {
        revert ConnextRouter__xReceive_notReceivedAssetBalance();
      } else {
        _tempTokenToCheck = Snapshot(asset, balance - amount);
      }

      /**
       * @dev Due to the AMM nature of Connext, there could be some slippage
       * incurred on the amount that this contract receives after bridging.
       * There is also a routing fee of 0.05% of the bridged amount.
       * The slippage can't be calculated upfront so that's why we need to
       * replace `amount` in the encoded args for the first action if
       * the action is Deposit, or Payback.
       */
      (args[0], beforeSlipped) = _accountForSlippage(amount, actions[0], args[0]);
    }

    /**
     * @dev Connext will keep the custody of the bridged amount if the call
     * to `xReceive` fails. That's why we need to ensure the funds are not stuck at Connext.
     * Therefore we try/catch instead of directly calling _bundleInternal(...).
     */
    try this.xBundleConnext(actions, args, beforeSlipped) {
      emit XReceived(transferId, originDomain, true, asset, amount, callData);
    } catch {
      if (balance > 0) {
        SafeERC20.safeTransfer(IERC20(asset), address(handler), balance);
        handler.recordFailed(transferId, amount, asset, originSender, originDomain, actions, args);
      }

      // Ensure clear storage for token balance checks.
      delete _tempTokenToCheck;
      emit XReceived(transferId, originDomain, false, asset, amount, callData);
    }

    return "";
  }

  /**
   * @notice Function selector created to allow try-catch procedure in Connext message data
   * passing.Including argument for `beforeSlipepd` not available in {BaseRouter-xBundle}.
   *
   * @param actions an array of actions that will be executed in a row
   * @param args an array of encoded inputs needed to execute each action
   * @param beforeSlipped amount passed by the origin cross-chain router operation
   *
   * @dev Requirements:
   * - Must only be called within the context of this same contract.
   */
  function xBundleConnext(
    Action[] calldata actions,
    bytes[] calldata args,
    uint256 beforeSlipped
  )
    external
    payable
    onlySelf
  {
    _bundleInternal(actions, args, beforeSlipped);
  }

  /**
   * @dev Decodes and replaces "amount" argument in args with `receivedAmount`
   * in Deposit, or Payback.
   *
   * Refer to:
   * https://github.com/Fujicracy/fuji-v2/issues/253#issuecomment-1385995095
   */
  function _accountForSlippage(
    uint256 receivedAmount,
    Action action,
    bytes memory args
  )
    internal
    pure
    returns (bytes memory newArgs, uint256 beforeSlipped)
  {
    newArgs = args;

    // Check first action type and replace with slippage-amount.
    if (action == Action.Deposit || action == Action.Payback) {
      // For Deposit or Payback.
      (IVault vault, uint256 amount, address receiver, address sender) =
        abi.decode(args, (IVault, uint256, address, address));

      if (amount != receivedAmount) {
        beforeSlipped = amount;
        newArgs = abi.encode(vault, receivedAmount, receiver, sender);
      }
    } else if (action == Action.WithdrawETH) {
      // For WithdrawETH
      (uint256 amount, address receiver) = abi.decode(args, (uint256, address));
      if (amount != receivedAmount) {
        beforeSlipped = amount;
        newArgs = abi.encode(receivedAmount, receiver);
      }
    }
  }

  /// @inheritdoc BaseRouter
  function _crossTransfer(
    bytes memory params,
    address beneficiary
  )
    internal
    override
    returns (address)
  {
    (
      uint256 destDomain,
      uint256 slippage,
      address asset,
      uint256 amount,
      address receiver,
      address sender
    ) = abi.decode(params, (uint256, uint256, address, uint256, address, address));

    address beneficiary_ = _checkBeneficiary(beneficiary, receiver);

    _safePullTokenFrom(asset, sender, amount);
    _safeApprove(asset, address(connext), amount);

    bytes32 transferId = connext.xcall(
      // _destination: Domain ID of the destination chain
      uint32(destDomain),
      // _to: address of the target contract
      receiver,
      // _asset: address of the token contract
      asset,
      // _delegate: address that has rights to update the original slippage tolerance
      // by calling Connext's forceUpdateSlippage function
      beneficiary_,
      // _amount: amount of tokens to transfer
      amount,
      // _slippage: can be anything between 0-10000 becaus
      // the maximum amount of slippage the user will accept in BPS, 30 == 0.3%
      slippage,
      // _callData: empty because we're only sending funds
      ""
    );
    emit XCalled(transferId, msg.sender, receiver, destDomain, asset, amount, "");

    return beneficiary_;
  }

  /// @inheritdoc BaseRouter
  function _crossTransferWithCalldata(
    bytes memory params,
    address beneficiary
  )
    internal
    override
    returns (address beneficiary_)
  {
    (
      uint256 destDomain,
      uint256 slippage,
      address asset,
      uint256 amount,
      address sender,
      bytes memory callData
    ) = abi.decode(params, (uint256, uint256, address, uint256, address, bytes));

    (Action[] memory actions, bytes[] memory args,) =
      abi.decode(callData, (Action[], bytes[], uint256));

    beneficiary_ = _checkBeneficiary(beneficiary, _getBeneficiaryFromCalldata(actions, args));

    _safePullTokenFrom(asset, sender, amount);
    _safeApprove(asset, address(connext), amount);

    bytes32 transferId = connext.xcall(
      // _destination: Domain ID of the destination chain
      uint32(destDomain),
      // _to: address of the target contract
      routerByDomain[destDomain],
      // _asset: address of the token contract
      asset,
      // _delegate: address that can revert or forceLocal on destination
      beneficiary_,
      // _amount: amount of tokens to transfer
      amount,
      // _slippage: can be anything between 0-10000 becaus
      // the maximum amount of slippage the user will accept in BPS, 30 == 0.3%
      slippage,
      // _callData: the encoded calldata to send
      callData
    );

    emit XCalled(
      transferId, msg.sender, routerByDomain[destDomain], destDomain, asset, amount, callData
    );

    return beneficiary_;
  }

  /**
   * @dev Returns who is the first receiver of value in `callData`
   * Requirements:
   * - Must revert if "swap" is first action
   *
   * @param actions to execute in {BaseRouter-xBundle}
   * @param args to execute in {BaseRouter-xBundle}
   */
  function _getBeneficiaryFromCalldata(
    Action[] memory actions,
    bytes[] memory args
  )
    internal
    view
    override
    returns (address beneficiary_)
  {
    if (actions[0] == Action.Deposit || actions[0] == Action.Payback) {
      // For Deposit or Payback.
      (,, address receiver,) = abi.decode(args[0], (IVault, uint256, address, address));
      beneficiary_ = receiver;
    } else if (actions[0] == Action.Withdraw || actions[0] == Action.Borrow) {
      // For Withdraw or Borrow
      (,,, address owner) = abi.decode(args[0], (IVault, uint256, address, address));
      beneficiary_ = owner;
    } else if (actions[0] == Action.WithdrawETH) {
      // For WithdrawEth
      (, address receiver) = abi.decode(args[0], (uint256, address));
      beneficiary_ = receiver;
    } else if (actions[0] == Action.PermitBorrow || actions[0] == Action.PermitWithdraw) {
      (, address owner,,,,,,) = abi.decode(
        args[0], (IVaultPermissions, address, address, uint256, uint256, uint8, bytes32, bytes32)
      );
      beneficiary_ = owner;
    } else if (actions[0] == Action.Flashloan) {
      (,,,, bytes memory requestorCalldata) =
        abi.decode(args[0], (IFlasher, address, uint256, address, bytes));

      (Action[] memory newActions, bytes[] memory newArgs) = abi.decode(
        LibBytes.slice(requestorCalldata, 4, requestorCalldata.length - 4), (Action[], bytes[])
      );

      beneficiary_ = _getBeneficiaryFromCalldata(newActions, newArgs);
    } else if (actions[0] == Action.XTransfer) {
      (,,,, address receiver,) =
        abi.decode(args[0], (uint256, uint256, address, uint256, address, address));
      beneficiary_ = receiver;
    } else if (actions[0] == Action.XTransferWithCall) {
      (,,,, bytes memory callData) =
        abi.decode(args[0], (uint256, uint256, address, uint256, bytes));

      (Action[] memory actions_, bytes[] memory args_,) =
        abi.decode(callData, (Action[], bytes[], uint256));

      beneficiary_ = _getBeneficiaryFromCalldata(actions_, args_);
    } else if (actions[0] == Action.DepositETH) {
      /// @dev There is no beneficiary in depositETH, therefore we do a recurssion with i = 1
      uint256 len = actions.length;

      Action[] memory chopActions = new Action[](len -1);
      bytes[] memory chopArgs = new bytes[](len -1);

      for (uint256 i = 1; i < len;) {
        chopActions[i - 1] = actions[i];
        chopArgs[i - 1] = args[i];
        unchecked {
          ++i;
        }
      }
      beneficiary_ = _getBeneficiaryFromCalldata(chopActions, chopArgs);
    } else if (actions[0] == Action.Swap) {
      /// @dev swap cannot be actions[0].
      revert BaseRouter__bundleInternal_swapNotFirstAction();
    }
  }

  /**
   * @notice Anyone can call this function on the origin domain to increase the relayer fee for a transfer.
   *
   * @param transferId the unique identifier of the crosschain transaction
   */
  function bumpTransfer(bytes32 transferId) external payable {
    connext.bumpTransfer{value: msg.value}(transferId);
  }

  /**
   * @notice Registers an address of this contract deployed on another chain.
   *
   * @param domain unique identifier of a chain as defined in
   * https://docs.connext.network/resources/deployments
   * @param router address of a router deployed on the chain defined by its domain
   *
   * @dev The mapping domain -> router is used in `xReceive` to verify the origin sender.
   * Requirements:
   *  - Must be restricted to timelock.
   *  - `router` must be a non-zero address.
   */
  function setRouter(uint256 domain, address router) external onlyTimelock {
    if (router == address(0)) {
      revert ConnextRouter__setRouter_invalidInput();
    }
    routerByDomain[domain] = router;

    emit NewRouterAdded(router, domain);
  }
}

File 2 of 25 : SafeERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";
import "../extensions/IERC20Permit.sol";
import "../../../utils/Address.sol";

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

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

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

    /**
     * @dev Deprecated. This function has issues similar to the ones found in
     * {IERC20-approve}, and its usage is discouraged.
     *
     * Whenever possible, use {safeIncreaseAllowance} and
     * {safeDecreaseAllowance} instead.
     */
    function safeApprove(IERC20 token, address spender, uint256 value) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        require(
            (value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

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

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
        }
    }

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

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

    /**
     * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
     * Revert on invalid signature.
     */
    function safePermit(
        IERC20Permit token,
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        uint256 nonceBefore = token.nonces(owner);
        token.permit(owner, spender, value, deadline, v, r, s);
        uint256 nonceAfter = token.nonces(owner);
        require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
        require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
        // and not revert is the subcall reverts.

        (bool success, bytes memory returndata) = address(token).call(data);
        return
            success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
    }
}

File 3 of 25 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

File 4 of 25 : IConnext.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.15;

/**
 * @title IConnext
 *
 * @author Fujidao Labs
 *
 * @notice Defines the common interfaces and data types used
 * to interact with Connext Amarok.
 */

/**
 * @notice These are the parameters that will remain constant between the
 * two chains. They are supplied on `xcall` and should be asserted on `execute`
 * @property to - The account that receives funds, in the event of a crosschain call,
 * will receive funds if the call fails.
 *
 * @param originDomain - The originating domain (i.e. where `xcall` is called). Must match nomad domain schema
 * @param destinationDomain - The final domain (i.e. where `execute` / `reconcile` are called). Must match nomad domain schema
 * @param canonicalDomain - The canonical domain of the asset you are bridging
 * @param to - The address you are sending funds (and potentially data) to
 * @param delegate - An address who can execute txs on behalf of `to`, in addition to allowing relayers
 * @param receiveLocal - If true, will use the local nomad asset on the destination instead of adopted.
 * @param callData - The data to execute on the receiving chain. If no crosschain call is needed, then leave empty.
 * @param slippage - Slippage user is willing to accept from original amount in expressed in BPS (i.e. if
 * a user takes 1% slippage, this is expressed as 1_000)
 * @param originSender - The msg.sender of the xcall
 * @param bridgedAmt - The amount sent over the bridge (after potential AMM on xcall)
 * @param normalizedIn - The amount sent to `xcall`, normalized to 18 decimals
 * @param nonce - The nonce on the origin domain used to ensure the transferIds are unique
 * @param canonicalId - The unique identifier of the canonical token corresponding to bridge assets
 */
struct TransferInfo {
  uint32 originDomain;
  uint32 destinationDomain;
  uint32 canonicalDomain;
  address to;
  address delegate;
  bool receiveLocal;
  bytes callData;
  uint256 slippage;
  address originSender;
  uint256 bridgedAmt;
  uint256 normalizedIn;
  uint256 nonce;
  bytes32 canonicalId;
}

/**
 * @notice
 * @param params - The TransferInfo. These are consistent across sending and receiving chains.
 * @param routers - The routers who you are sending the funds on behalf of.
 * @param routerSignatures - Signatures belonging to the routers indicating permission to use funds
 * for the signed transfer ID.
 * @param sequencer - The sequencer who assigned the router path to this transfer.
 * @param sequencerSignature - Signature produced by the sequencer for path assignment accountability
 * for the path that was signed.
 */
struct ExecuteArgs {
  TransferInfo params;
  address[] routers;
  bytes[] routerSignatures;
  address sequencer;
  bytes sequencerSignature;
}

interface IConnext {
  function xcall(
    uint32 _destination,
    address _to,
    address _asset,
    address _delegate,
    uint256 _amount,
    uint256 _slippage,
    bytes calldata _callData
  )
    external
    payable
    returns (bytes32);

  function execute(ExecuteArgs calldata _args)
    external
    returns (bool success, bytes memory returnData);

  function bumpTransfer(bytes32 transferId) external payable;

  function forceUpdateSlippage(TransferInfo calldata _params, uint256 _slippage) external;
}

interface IXReceiver {
  function xReceive(
    bytes32 _transferId,
    uint256 _amount,
    address _asset,
    address _originSender,
    uint32 _origin,
    bytes memory _callData
  )
    external
    returns (bytes memory);
}

File 5 of 25 : ConnextHandler.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.15;

/**
 * @title ConnextHandler
 *
 * @author Fujidao Labs
 *
 * @notice Handles failed transactions from Connext and keeps custody of
 * the transferred funds.
 */

import {ConnextRouter} from "./ConnextRouter.sol";
import {IRouter} from "../interfaces/IRouter.sol";
import {IVault} from "../interfaces/IVault.sol";
import {ISwapper} from "../interfaces/ISwapper.sol";
import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";

contract ConnextHandler {
  /**
   * @dev Contains the information of a failed transaction.
   */
  struct FailedTxn {
    bytes32 transferId;
    uint256 amount;
    address asset;
    address originSender;
    uint32 originDomain;
    IRouter.Action[] actions;
    bytes[] args;
    uint128 nonce;
    bool executed;
  }

  /**
   * @dev Emitted when a failed transaction is recorded.
   *
   * @param transferId  unique id of the cross-chain txn
   * @param amount transferred
   * @param asset being transferred
   * @param originSender of the cross-chain txn
   * @param originDomain of the cross-chain txn
   * @param actions to be called in xBundle
   * @param args to be called for each action in xBundle
   * @param nonce of failed txn
   */
  event FailedTxnRecorded(
    bytes32 indexed transferId,
    uint256 amount,
    address asset,
    address originSender,
    uint32 originDomain,
    IRouter.Action[] actions,
    bytes[] args,
    uint128 nonce
  );

  /**
   * @dev Emitted when a failed transaction gets retried.
   *
   * @param transferId the unique identifier of the cross-chain txn
   * @param success boolean
   * @param oldArgs of the failed transaction
   * @param newArgs attemped in execution
   */
  event FailedTxnExecuted(
    bytes32 indexed transferId,
    IRouter.Action[] oldActions,
    IRouter.Action[] newActions,
    bytes[] oldArgs,
    bytes[] newArgs,
    uint128 nonce,
    bool indexed success
  );

  /// @dev Custom errors
  error ConnextHandler__callerNotConnextRouter();
  error ConnextHandler__executeFailed_emptyTxn();
  error ConnextHandler__executeFailed_tranferAlreadyExecuted(bytes32 transferId, uint128 nonce);

  bytes32 private constant ZERO_BYTES32 =
    0x0000000000000000000000000000000000000000000000000000000000000000;

  ConnextRouter public immutable connextRouter;

  /**
   * @dev Maps a failed transferId -> nonce -> calldata
   * Multiple failed attempts are registered with nonce
   */
  mapping(bytes32 => mapping(uint256 => FailedTxn)) private _failedTxns;

  modifier onlyConnextRouter() {
    if (msg.sender != address(connextRouter)) {
      revert ConnextHandler__callerNotConnextRouter();
    }
    _;
  }

  /// @dev Modifier that checks `msg.sender` is an allowed called in {ConnextRouter}.
  modifier onlyAllowedCaller() {
    if (!connextRouter.isAllowedCaller(msg.sender)) {
      revert ConnextHandler__callerNotConnextRouter();
    }
    _;
  }

  /**
   * @notice Constructor that initialized
   */
  constructor(address connextRouter_) {
    connextRouter = ConnextRouter(payable(connextRouter_));
  }

  /**
   * @notice Returns the struct of failed transaction by `transferId`.
   *
   * @param transferId the unique identifier of the cross-chain txn
   * @param nonce attempt of failed tx
   */
  function getFailedTxn(bytes32 transferId, uint128 nonce) public view returns (FailedTxn memory) {
    return _failedTxns[transferId][nonce];
  }

  function getFailedTxnNextNonce(bytes32 transferId) public view returns (uint128 next) {
    next = 0;
    for (uint256 i; i < type(uint8).max;) {
      if (!isTransferIdRecorded(transferId, uint128(i))) {
        next = uint128(i);
        break;
      }
      unchecked {
        ++i;
      }
    }
  }

  /**
   * @notice Returns the true if the failed transaction is already recorded.
   *
   * @param transferId the unique identifier of the cross-chain txn
   */
  function isTransferIdRecorded(bytes32 transferId, uint128 nonce) public view returns (bool) {
    FailedTxn memory ftxn = _failedTxns[transferId][nonce];
    if (ftxn.transferId != ZERO_BYTES32 && ftxn.originDomain != 0) {
      return true;
    } else {
      return false;
    }
  }

  /**
   * @notice Records a failed {ConnextRouter-xReceive} call.
   *
   * @param transferId the unique identifier of the cross-chain txn
   * @param amount the amount of transferring asset, after slippage, the recipient address receives
   * @param asset the asset being transferred
   * @param originSender the address of the contract or EOA that called xcall on the origin chain
   * @param originDomain the origin domain identifier according Connext nomenclature
   * @param actions that should be executed in {BaseRouter-internalBundle}
   * @param args for the actions
   *
   * @dev At this point of execution {ConnextRouter} sent all balance of `asset` to this contract.
   * It has already been verified that `amount` of `asset` is >= to balance sent.
   * This function does not need to emit an event since {ConnextRouter} already emit
   * a failed `XReceived` event.
   */
  function recordFailed(
    bytes32 transferId,
    uint256 amount,
    address asset,
    address originSender,
    uint32 originDomain,
    IRouter.Action[] memory actions,
    bytes[] memory args
  )
    external
    onlyConnextRouter
  {
    uint128 nextNonce = getFailedTxnNextNonce(transferId);
    _failedTxns[transferId][nextNonce] = FailedTxn(
      transferId, amount, asset, originSender, originDomain, actions, args, nextNonce, false
    );

    emit FailedTxnRecorded(
      transferId, amount, asset, originSender, originDomain, actions, args, nextNonce
    );
  }

  /**
   * @notice Executes a failed transaction with update `args`
   *
   * @param transferId the unique identifier of the cross-chain txn
   * @param nonce of the failed attempt to execute
   * @param actions  that will replace actions of failed txn
   * @param args taht will replace args of failed txn
   *
   * @dev Requirements:
   * - Must only be called by an allowed caller in {ConnextRouter}.
   * - Must clear the txn from `_failedTxns` mapping if execution succeeds.
   * - Must replace `sender` in `args` for value tranfer type actions (Deposit-Payback-Swap}.
   */
  function executeFailedWithUpdatedArgs(
    bytes32 transferId,
    uint128 nonce,
    IRouter.Action[] memory actions,
    bytes[] memory args
  )
    external
    onlyAllowedCaller
  {
    FailedTxn memory txn = _failedTxns[transferId][nonce];

    if (txn.transferId == ZERO_BYTES32 || txn.originDomain == 0) {
      revert ConnextHandler__executeFailed_emptyTxn();
    } else if (txn.executed) {
      revert ConnextHandler__executeFailed_tranferAlreadyExecuted(transferId, nonce);
    }

    IERC20(txn.asset).approve(address(connextRouter), txn.amount);

    try connextRouter.xBundle(actions, args) {
      txn.executed = true;
      _failedTxns[transferId][nonce] = txn;
      emit FailedTxnExecuted(transferId, txn.actions, actions, txn.args, args, nonce, true);
    } catch {
      emit FailedTxnExecuted(transferId, txn.actions, actions, txn.args, args, nonce, false);
    }
  }
}

File 6 of 25 : BaseRouter.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.15;

/**
 * @title BaseRouter
 *
 * @author Fujidao Labs
 *
 * @notice Abstract contract to be inherited by all routers.
 */

import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol";
import {ERC20} from "openzeppelin-contracts/contracts/token/ERC20/ERC20.sol";
import {IRouter} from "../interfaces/IRouter.sol";
import {ISwapper} from "../interfaces/ISwapper.sol";
import {IVault} from "../interfaces/IVault.sol";
import {IChief} from "../interfaces/IChief.sol";
import {IFlasher} from "../interfaces/IFlasher.sol";
import {IVaultPermissions} from "../interfaces/IVaultPermissions.sol";
import {SystemAccessControl} from "../access/SystemAccessControl.sol";
import {IWETH9} from "../abstracts/WETH9.sol";
import {LibBytes} from "../libraries/LibBytes.sol";

abstract contract BaseRouter is SystemAccessControl, IRouter {
  /**
   * @dev Contains an address of an ERC-20 and the balance the router holds
   * at a given moment of the transaction (ref. `_tokensToCheck`).
   */
  struct Snapshot {
    address token;
    uint256 balance;
  }

  /**
   * @dev Struct used internally containing the arguments of a IRouter.Action.Permit* to store
   * and pass in memory and avoid "stack too deep" errors.
   */
  struct PermitArgs {
    IVaultPermissions vault;
    address owner;
    address receiver;
    uint256 amount;
    uint256 deadline;
    uint8 v;
    bytes32 r;
    bytes32 s;
  }

  /**
   * @dev Emitted when `caller` is updated according to `allowed` boolean
   * to perform cross-chain calls.
   *
   * @param caller permitted for cross-chain calls
   * @param allowed boolean state
   */
  event AllowCaller(address caller, bool allowed);

  /// @dev Custom Errors
  error BaseRouter__bundleInternal_swapNotFirstAction();
  error BaseRouter__bundleInternal_paramsMismatch();
  error BaseRouter__bundleInternal_flashloanInvalidRequestor();
  error BaseRouter__bundleInternal_noBalanceChange();
  error BaseRouter__bundleInternal_insufficientETH();
  error BaseRouter__bundleInternal_notBeneficiary();
  error BaseRouter__checkVaultInput_notActiveVault();
  error BaseRouter__bundleInternal_notAllowedSwapper();
  error BaseRouter__bundleInternal_notAllowedFlasher();
  error BaseRouter__handlePermit_notPermitAction();
  error BaseRouter__safeTransferETH_transferFailed();
  error BaseRouter__receive_senderNotWETH();
  error BaseRouter__fallback_notAllowed();
  error BaseRouter__allowCaller_zeroAddress();
  error BaseRouter__allowCaller_noAllowChange();
  error BaseRouter__bundleInternal_insufficientFlashloanBalance();

  IWETH9 public immutable WETH9;

  bytes32 private constant ZERO_BYTES32 =
    0x0000000000000000000000000000000000000000000000000000000000000000;

  /// @dev Apply it on entry cross-chain calls functions as required.
  mapping(address => bool) public isAllowedCaller;

  /**
   * @dev Stores token balances of this contract at a given moment.
   * It's used to pass tokens to check from parent contract this contract.
   */
  Snapshot internal _tempTokenToCheck;

  /**
   * @notice Constructor of a new {BaseRouter}.
   *
   * @param weth wrapped native token of this chain
   * @param chief contract
   */
  constructor(IWETH9 weth, IChief chief) payable SystemAccessControl(address(chief)) {
    WETH9 = weth;
  }

  /// @inheritdoc IRouter
  function xBundle(Action[] calldata actions, bytes[] calldata args) external payable override {
    _bundleInternal(actions, args, 0);
  }

  /**
   * @notice Marks a specific caller as allowed/disallowed to call certain functions.
   *
   * @param caller address to allow/disallow
   * @param allowed 'true' to allow, 'false' to disallow
   *
   * @dev The authorization is to be implemented on the bridge-specific contract.
   */
  function allowCaller(address caller, bool allowed) external onlyTimelock {
    _allowCaller(caller, allowed);
  }

  /// @inheritdoc IRouter
  function sweepToken(ERC20 token, address receiver) external onlyHouseKeeper {
    SafeERC20.safeTransfer(token, receiver, token.balanceOf(address(this)));
  }

  /// @inheritdoc IRouter
  function sweepETH(address receiver) external onlyHouseKeeper {
    _safeTransferETH(receiver, address(this).balance);
  }

  /**
   * @dev Executes a bundle of actions.
   * Requirements:
   * - Must not leave any balance in this contract after all actions.
   * - Must call `_checkNoBalanceChange()` after all `actions` are executed.
   * - Must call `_addTokenToList()` in `actions` that involve tokens.
   * - Must clear `_beneficiary` from storage after all `actions` are executed.
   *
   * @param actions an array of actions that will be executed in a row
   * @param args an array of encoded inputs needed to execute each action
   * @param beforeSlipped amount passed by the origin cross-chain router operation
   */
  function _bundleInternal(
    Action[] memory actions,
    bytes[] memory args,
    uint256 beforeSlipped
  )
    internal
  {
    uint256 len = actions.length;
    if (len != args.length) {
      revert BaseRouter__bundleInternal_paramsMismatch();
    }

    /**
     * @dev Operations in the bundle should "benefit" or be executed
     * on behalf of this account. These are receivers on DEPOSIT and PAYBACK
     * or owners on WITHDRAW and BORROW.
     */
    address beneficiary;

    /**
     * @dev Hash generated during execution of "_bundleInternal()" that should
     * match the signed permit.
     * This argument is used in {VaultPermissions-PermitWithdraw} and
     * {VaultPermissions-PermitBorrow}
     */
    bytes32 actionArgsHash;

    /**
     * @dev Stores token balances of this contract at a given moment.
     * It's used to ensure there're no changes in balances at the
     * end of a transaction.
     */
    Snapshot[] memory tokensToCheck = new Snapshot[](10);

    /// @dev Add token to check from parent calls.
    if (_tempTokenToCheck.token != address(0)) {
      tokensToCheck[0] = _tempTokenToCheck;
    }

    uint256 nativeBalance = address(this).balance - msg.value;

    for (uint256 i; i < len;) {
      Action action = actions[i];
      if (action == Action.Deposit) {
        // DEPOSIT
        (IVault vault, uint256 amount, address receiver, address sender) =
          abi.decode(args[i], (IVault, uint256, address, address));

        _checkVaultInput(address(vault));

        address token = vault.asset();
        beneficiary = _checkBeneficiary(beneficiary, receiver);
        tokensToCheck = _addTokenToList(token, tokensToCheck);
        _safePullTokenFrom(token, sender, amount);
        _safeApprove(token, address(vault), amount);

        vault.deposit(amount, receiver);
      } else if (action == Action.Withdraw) {
        // WITHDRAW
        (IVault vault, uint256 amount, address receiver, address owner) =
          abi.decode(args[i], (IVault, uint256, address, address));

        _checkVaultInput(address(vault));

        beneficiary = _checkBeneficiary(beneficiary, owner);
        tokensToCheck = _addTokenToList(vault.asset(), tokensToCheck);

        vault.withdraw(amount, receiver, owner);
      } else if (action == Action.Borrow) {
        // BORROW
        (IVault vault, uint256 amount, address receiver, address owner) =
          abi.decode(args[i], (IVault, uint256, address, address));

        _checkVaultInput(address(vault));

        beneficiary = _checkBeneficiary(beneficiary, owner);
        tokensToCheck = _addTokenToList(vault.debtAsset(), tokensToCheck);

        vault.borrow(amount, receiver, owner);
      } else if (action == Action.Payback) {
        // PAYBACK
        (IVault vault, uint256 amount, address receiver, address sender) =
          abi.decode(args[i], (IVault, uint256, address, address));

        _checkVaultInput(address(vault));

        address token = vault.debtAsset();
        beneficiary = _checkBeneficiary(beneficiary, receiver);
        tokensToCheck = _addTokenToList(token, tokensToCheck);
        _safePullTokenFrom(token, sender, amount);
        _safeApprove(token, address(vault), amount);

        vault.payback(amount, receiver);
      } else if (action == Action.PermitWithdraw) {
        // PERMIT WITHDRAW
        if (actionArgsHash == ZERO_BYTES32) {
          actionArgsHash = _getActionArgsHash(actions, args, beforeSlipped);
        }

        // Scoped code in new private function to avoid "Stack too deep"
        address owner_ = _handlePermitAction(action, args[i], actionArgsHash);
        beneficiary = _checkBeneficiary(beneficiary, owner_);
      } else if (action == Action.PermitBorrow) {
        // PERMIT BORROW
        if (actionArgsHash == ZERO_BYTES32) {
          actionArgsHash = _getActionArgsHash(actions, args, beforeSlipped);
        }

        // Scoped code in new private function to avoid "Stack too deep"
        address owner_ = _handlePermitAction(action, args[i], actionArgsHash);
        beneficiary = _checkBeneficiary(beneficiary, owner_);
      } else if (action == Action.XTransfer) {
        // SIMPLE BRIDGE TRANSFER

        beneficiary = _crossTransfer(args[i], beneficiary);
      } else if (action == Action.XTransferWithCall) {
        // BRIDGE WITH CALLDATA

        beneficiary = _crossTransferWithCalldata(args[i], beneficiary);
      } else if (action == Action.Swap) {
        // SWAP

        if (i == 0) {
          /// @dev swap cannot be actions[0].
          revert BaseRouter__bundleInternal_swapNotFirstAction();
        }

        (beneficiary, tokensToCheck) = _handleSwapAction(args[i], beneficiary, tokensToCheck);
      } else if (action == Action.Flashloan) {
        // FLASHLOAN

        // Decode params.
        (
          IFlasher flasher,
          address asset,
          uint256 flashAmount,
          address requestor,
          bytes memory requestorCalldata
        ) = abi.decode(args[i], (IFlasher, address, uint256, address, bytes));

        if (!chief.allowedFlasher(address(flasher))) {
          revert BaseRouter__bundleInternal_notAllowedFlasher();
        }
        if (requestor != address(this)) {
          revert BaseRouter__bundleInternal_flashloanInvalidRequestor();
        }
        tokensToCheck = _addTokenToList(asset, tokensToCheck);

        (Action[] memory innerActions, bytes[] memory innerArgs) = abi.decode(
          LibBytes.slice(requestorCalldata, 4, requestorCalldata.length - 4), (Action[], bytes[])
        );

        beneficiary = _getBeneficiaryFromCalldata(innerActions, innerArgs);

        // Call Flasher.
        flasher.initiateFlashloan(asset, flashAmount, requestor, requestorCalldata);
      } else if (action == Action.DepositETH) {
        uint256 amount = abi.decode(args[i], (uint256));

        if (amount != msg.value) {
          revert BaseRouter__bundleInternal_insufficientETH();
        }
        tokensToCheck = _addTokenToList(address(WETH9), tokensToCheck);

        WETH9.deposit{value: msg.value}();
      } else if (action == Action.WithdrawETH) {
        (uint256 amount, address receiver) = abi.decode(args[i], (uint256, address));
        beneficiary = _checkBeneficiary(beneficiary, receiver);
        tokensToCheck = _addTokenToList(address(WETH9), tokensToCheck);

        WETH9.withdraw(amount);

        _safeTransferETH(receiver, amount);
      }
      unchecked {
        ++i;
      }
    }
    _checkNoBalanceChange(tokensToCheck, nativeBalance);
  }

  /**
   * @dev Handles both permit actions logic flow.
   * This function was required to avoid "stack too deep" error in `_bundleInternal()`.
   *
   * @param action either IRouter.Action.PermitWithdraw (6), or IRouter.Action.PermitBorrow (7)
   * @param arg of the ongoing action
   * @param actionArgsHash_ created previously withing `_bundleInternal()` to be used in permit check
   */
  function _handlePermitAction(
    IRouter.Action action,
    bytes memory arg,
    bytes32 actionArgsHash_
  )
    private
    returns (address)
  {
    PermitArgs memory permitArgs;
    {
      (
        permitArgs.vault,
        permitArgs.owner,
        permitArgs.receiver,
        permitArgs.amount,
        permitArgs.deadline,
        permitArgs.v,
        permitArgs.r,
        permitArgs.s
      ) = abi.decode(
        arg, (IVaultPermissions, address, address, uint256, uint256, uint8, bytes32, bytes32)
      );
    }

    _checkVaultInput(address(permitArgs.vault));

    if (action == IRouter.Action.PermitWithdraw) {
      permitArgs.vault.permitWithdraw(
        permitArgs.owner,
        permitArgs.receiver,
        permitArgs.amount,
        permitArgs.deadline,
        actionArgsHash_,
        permitArgs.v,
        permitArgs.r,
        permitArgs.s
      );
    } else if (action == IRouter.Action.PermitBorrow) {
      permitArgs.vault.permitBorrow(
        permitArgs.owner,
        permitArgs.receiver,
        permitArgs.amount,
        permitArgs.deadline,
        actionArgsHash_,
        permitArgs.v,
        permitArgs.r,
        permitArgs.s
      );
    } else {
      revert BaseRouter__handlePermit_notPermitAction();
    }

    return permitArgs.owner;
  }

  /**
   * @dev Returns the `zeroPermitEncodedArgs` which is required to create
   * the `actionArgsHash` used during permit signature
   *
   * @param vault that will execute action
   * @param owner owner of the assets
   * @param receiver of the assets after action
   * @param amount of assets being permitted in action
   */
  function _getZeroPermitEncodedArgs(
    IVaultPermissions vault,
    address owner,
    address receiver,
    uint256 amount
  )
    private
    pure
    returns (bytes memory)
  {
    return abi.encode(vault, owner, receiver, amount, 0, 0, ZERO_BYTES32, ZERO_BYTES32);
  }

  /**
   * @dev Returns the `actionsArgsHash` required in
   * {VaultPermissions-permitWithdraw} or {VaultPermissions-permitBorrow}.
   * Requirements:
   * - Must replace arguments in IRouter.Action.PermitWithdraw for "zeroPermit".
   * - Must replace arguments in IRouter.Action.PermitBorrow for "zeroPermit".
   * - Must replace `beforeSlipped` amount in cross-chain txs that had slippage.
   *
   *
   * @param actions being executed in this `_bundleInternal`
   * @param args provided in `_bundleInternal`
   * @param beforeSlipped amount passed by the origin cross-chain router operation
   */
  function _getActionArgsHash(
    IRouter.Action[] memory actions,
    bytes[] memory args,
    uint256 beforeSlipped
  )
    private
    pure
    returns (bytes32)
  {
    uint256 len = actions.length;

    /**
     * @dev We intend to ONLY modify the new bytes array.
     * "memory" in solidity persists within internal calls.
     */
    bytes[] memory modArgs = new bytes[](len);
    for (uint256 i; i < len; i++) {
      modArgs[i] = args[i];
      if (
        i == 0 && beforeSlipped != 0
          && (actions[i] == IRouter.Action.Deposit || actions[i] == IRouter.Action.Payback)
      ) {
        /**
         * @dev Replace slippage values in the first ( i==0 ) "value" transfer
         * action in the destination chain (deposit or to payback).
         * If `beforeSlipped` == 0, it means there was no slippage in the attempted cross-chain tx
         * or the tx is single chain; thereore, not requiring any replacement.
         * Then, if beforeSlipped != 0 and beforeSlipped != slippedAmount, function should replace
         * to obtain the "original" intended transfer value signed in `actionArgsHash`.
         */
        (IVault vault, uint256 slippedAmount, address receiver, address sender) =
          abi.decode(modArgs[i], (IVault, uint256, address, address));
        if (beforeSlipped != slippedAmount) {
          modArgs[i] = abi.encode(vault, beforeSlipped, receiver, sender);
        }
      }
      if (actions[i] == IRouter.Action.PermitWithdraw || actions[i] == IRouter.Action.PermitBorrow)
      {
        // Need to replace permit `args` at `index` with the `zeroPermitArg`.
        (IVaultPermissions vault, address owner, address receiver, uint256 amount,,,,) = abi.decode(
          modArgs[i],
          (IVaultPermissions, address, address, uint256, uint256, uint8, bytes32, bytes32)
        );
        modArgs[i] = _getZeroPermitEncodedArgs(vault, owner, receiver, amount);
      }
    }
    return keccak256(abi.encode(actions, modArgs));
  }

  /**
   * @dev Handles swap actions logic flow.
   * This function was required to avoid "stack too deep" error in `_bundleInternal()`.
   * Requirements:
   * - Must return updated "beneficiary" and "tokensToCheck".
   *
   * @param arg of the ongoing action
   * @param beneficiary_ passed through `_bundleInternal()`
   * @param tokensToCheck_ passed through `_bundleInternal()`
   */
  function _handleSwapAction(
    bytes memory arg,
    address beneficiary_,
    Snapshot[] memory tokensToCheck_
  )
    internal
    returns (address, Snapshot[] memory)
  {
    (
      ISwapper swapper,
      address assetIn,
      address assetOut,
      uint256 amountIn,
      uint256 amountOut,
      address receiver,
      address sweeper,
      uint256 minSweepOut
    ) = abi.decode(arg, (ISwapper, address, address, uint256, uint256, address, address, uint256));

    if (!chief.allowedSwapper(address(swapper))) {
      revert BaseRouter__bundleInternal_notAllowedSwapper();
    }

    tokensToCheck_ = _addTokenToList(assetIn, tokensToCheck_);
    tokensToCheck_ = _addTokenToList(assetOut, tokensToCheck_);
    _safeApprove(assetIn, address(swapper), amountIn);

    if (receiver != address(this) && !chief.allowedFlasher(receiver)) {
      beneficiary_ = _checkBeneficiary(beneficiary_, receiver);
    }

    if (sweeper != address(this)) {
      beneficiary_ = _checkBeneficiary(beneficiary_, sweeper);
    }

    swapper.swap(assetIn, assetOut, amountIn, amountOut, receiver, sweeper, minSweepOut);
    return (beneficiary_, tokensToCheck_);
  }

  /**
   * @dev Helper function to transfer ETH.
   *
   * @param receiver address to receive the ETH
   * @param amount amount to be transferred
   */
  function _safeTransferETH(address receiver, uint256 amount) internal {
    (bool success,) = receiver.call{value: amount}(new bytes(0));
    if (!success) {
      revert BaseRouter__safeTransferETH_transferFailed();
    }
  }

  /**
   * @dev Helper function to pull ERC-20 token from a sender address after some checks.
   * The checks are needed because when we bundle multiple actions
   * it can happen the router already holds the assets in question;
   * for. example when we withdraw from a vault and deposit to another one.
   *
   * @param token ERC-20 token address
   * @param sender address to pull tokens from
   * @param amount amount of tokens to be pulled
   */
  function _safePullTokenFrom(address token, address sender, uint256 amount) internal {
    if (sender != address(this) && sender == msg.sender) {
      SafeERC20.safeTransferFrom(ERC20(token), sender, address(this), amount);
    }
  }

  /**
   * @dev Helper function to approve ERC-20 transfers.
   *
   * @param token ERC-20 address to approve
   * @param to address to approve as a spender
   * @param amount amount to be approved
   */
  function _safeApprove(address token, address to, uint256 amount) internal {
    SafeERC20.safeApprove(ERC20(token), to, amount);
  }

  /**
   * @dev Check `allowCaller()` above.
   *
   * @param caller address to allow/disallow
   * @param allowed 'true' to allow, 'false' to disallow
   */
  function _allowCaller(address caller, bool allowed) internal {
    if (caller == address(0)) {
      revert BaseRouter__allowCaller_zeroAddress();
    }
    if (isAllowedCaller[caller] == allowed) {
      revert BaseRouter__allowCaller_noAllowChange();
    }
    isAllowedCaller[caller] = allowed;
    emit AllowCaller(caller, allowed);
  }

  /**
   * @dev Function to be implemented on the bridge-specific contract
   * used to transfer funds WITHOUT calldata to a destination chain.
   */
  function _crossTransfer(bytes memory, address beneficiary) internal virtual returns (address);

  /**
   * @dev Function to be implemented on the bridge-specific contract
   * used to transfer funds WITH calldata to a destination chain.
   */
  function _crossTransferWithCalldata(
    bytes memory,
    address beneficiary
  )
    internal
    virtual
    returns (address);

  /**
   * @dev Returns "true" and on what `index` if token has already
   * been added to `tokenList`.
   *
   * @param token address of ERC-20 to check
   * @param tokenList to check
   */
  function _isInTokenList(
    address token,
    Snapshot[] memory tokenList
  )
    private
    pure
    returns (bool value, uint256 index)
  {
    uint256 len = tokenList.length;
    for (uint256 i; i < len;) {
      if (token == tokenList[i].token) {
        value = true;
        index = i;
        break;
      }
      unchecked {
        ++i;
      }
    }
  }

  /**
   * @dev Adds a token and balance to a Snapshot and returns it.
   * Requirements:
   * - Must check if token has already been added.
   *
   * @param token address of ERC-20 to be pushed
   * @param tokenList to add token
   */
  function _addTokenToList(
    address token,
    Snapshot[] memory tokenList
  )
    private
    view
    returns (Snapshot[] memory)
  {
    (bool isInList, uint256 index) = _isInTokenList(token, tokenList);
    if (!isInList) {
      uint256 position = index == 0 ? index : index + 1;
      tokenList[position] = Snapshot(token, IERC20(token).balanceOf(address(this)));
    }
    return tokenList;
  }

  /**
   * @dev Checks that `erc20-balanceOf` of `_tokensToCheck` haven't change for this address.
   * Requirements:
   * - Must be called in `_bundleInternal()` at the end of all executed `actions`.
   * - Must clear `_tokensToCheck` from storage at the end of checks.
   *
   * @param tokensToCheck array of 'Snapshot' elements
   * @param nativeBalance the stored balance of ETH
   */
  function _checkNoBalanceChange(
    Snapshot[] memory tokensToCheck,
    uint256 nativeBalance
  )
    private
    view
  {
    uint256 len = tokensToCheck.length;
    for (uint256 i; i < len;) {
      if (tokensToCheck[i].token != address(0)) {
        uint256 previousBalance = tokensToCheck[i].balance;
        uint256 currentBalance = IERC20(tokensToCheck[i].token).balanceOf(address(this));

        if (currentBalance != previousBalance) {
          revert BaseRouter__bundleInternal_noBalanceChange();
        }
      }
      unchecked {
        ++i;
      }
    }

    // Check at the end the native balance.
    if (nativeBalance != address(this).balance) {
      revert BaseRouter__bundleInternal_noBalanceChange();
    }
  }

  /**
   * @dev When bundling multiple actions assure that we act for a single beneficiary;
   * receivers on DEPOSIT and PAYBACK and owners on WITHDRAW and BORROW
   * must be the same user
   *
   * @param user address to verify is the beneficiary
   */
  function _checkBeneficiary(address beneficiary, address user) internal pure returns (address) {
    if (beneficiary == address(0)) {
      return user;
    } else if (beneficiary != user) {
      revert BaseRouter__bundleInternal_notBeneficiary();
    } else {
      return user;
    }
  }

  /**
   * @dev Extracts the beneficiary from a set of actions and args.
   * Requirements:
   * - Must be implemented in child contract, and added to `_crossTransfer` and
   * `crossTansferWithCalldata` when applicable.
   *
   * @param actions an array of actions that will be executed in a row
   * @param args an array of encoded inputs needed to execute each action
   */
  function _getBeneficiaryFromCalldata(
    Action[] memory actions,
    bytes[] memory args
  )
    internal
    view
    virtual
    returns (address beneficiary_);

  function _checkVaultInput(address vault_) internal view {
    if (!chief.isVaultActive(vault_)) {
      revert BaseRouter__checkVaultInput_notActiveVault();
    }
  }

  /**
   * @dev Only WETH contract is allowed to transfer ETH to this address.
   * Prevent other addresses to send Ether to this contract.
   */
  receive() external payable {
    if (msg.sender != address(WETH9)) {
      revert BaseRouter__receive_senderNotWETH();
    }
  }

  /**
   * @dev Revert fallback calls.
   */
  fallback() external payable {
    revert BaseRouter__fallback_notAllowed();
  }
}

File 7 of 25 : WETH9.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.15;

/**
 * @title IWETH9
 *
 * @author Unknown
 *
 * @notice Abstract contract of add-on functions of a
 * typical ERC20 wrapped native token.
 */

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

abstract contract IWETH9 is ERC20 {
  /// @notice Deposit ether to get wrapped ether
  function deposit() external payable virtual;

  /// @notice Withdraw wrapped ether to get ether
  function withdraw(uint256) external virtual;
}

File 8 of 25 : IVault.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.15;

/**
 * @title IVault
 *
 * @author Fujidao Labs
 *
 * @notice Defines the interface for vaults extending from IERC4326.
 */

import {IERC4626} from "openzeppelin-contracts/contracts/interfaces/IERC4626.sol";
import {ILendingProvider} from "./ILendingProvider.sol";
import {IFujiOracle} from "./IFujiOracle.sol";

interface IVault is IERC4626 {
  /**
   * @dev Emit when borrow action occurs.
   *
   * @param sender who calls {IVault-borrow}
   * @param receiver of the borrowed 'debt' amount
   * @param owner who will incur the debt
   * @param debt amount
   * @param shares amount of 'debtShares' received
   */
  event Borrow(
    address indexed sender,
    address indexed receiver,
    address indexed owner,
    uint256 debt,
    uint256 shares
  );

  /**
   * @dev Emit when payback action occurs.
   *
   * @param sender address who calls {IVault-payback}
   * @param owner address whose debt will be reduced
   * @param debt amount
   * @param shares amound of 'debtShares' burned
   */
  event Payback(address indexed sender, address indexed owner, uint256 debt, uint256 shares);

  /**
   * @dev Emit when the vault is initialized
   *
   * @param initializer of this vault
   *
   */
  event VaultInitialized(address initializer);

  /**
   * @dev Emit when the oracle address is changed.
   *
   * @param newOracle the new oracle address
   */
  event OracleChanged(IFujiOracle newOracle);

  /**
   * @dev Emit when the available providers for the vault change.
   *
   * @param newProviders the new providers available
   */
  event ProvidersChanged(ILendingProvider[] newProviders);

  /**
   * @dev Emit when the active provider is changed.
   *
   * @param newActiveProvider the new active provider
   */
  event ActiveProviderChanged(ILendingProvider newActiveProvider);

  /**
   * @dev Emit when the vault is rebalanced.
   *
   * @param assets amount to be rebalanced
   * @param debt amount to be rebalanced
   * @param from provider
   * @param to provider
   */
  event VaultRebalance(uint256 assets, uint256 debt, address indexed from, address indexed to);

  /**
   * @dev Emit when the max LTV is changed.
   * See factors: https://github.com/Fujicracy/CrossFuji/tree/main/packages/protocol#readme.
   *
   * @param newMaxLtv the new max LTV
   */
  event MaxLtvChanged(uint256 newMaxLtv);

  /**
   * @dev Emit when the liquidation ratio is changed.
   * See factors: https://github.com/Fujicracy/CrossFuji/tree/main/packages/protocol#readme.
   *
   * @param newLiqRatio the new liquidation ratio
   */
  event LiqRatioChanged(uint256 newLiqRatio);

  /**
   * @dev Emit when the minumum amount is changed.
   *
   * @param newMinAmount the new minimum amount
   */
  event MinAmountChanged(uint256 newMinAmount);

  /**
   * @dev Emit when the deposit cap is changed.
   *
   * @param newDepositCap the new deposit cap of this vault
   */
  event DepositCapChanged(uint256 newDepositCap);

  /*///////////////////////////
    Asset management functions
  //////////////////////////*/

  /**
   * @notice Returns the amount of assets owned by `owner`.
   *
   * @param owner to check balance
   *
   * @dev This method avoids having to do external conversions from shares to
   * assets, since {IERC4626-balanceOf} returns shares.
   */
  function balanceOfAsset(address owner) external view returns (uint256 assets);

  /*///////////////////////////
    Debt management functions
  //////////////////////////*/

  /**
   * @notice Returns the decimals for 'debtAsset' of this vault.
   *
   * @dev Requirements:
   * - Must match the 'debtAsset' decimals in ERC20 token.
   * - Must return zero in a {YieldVault}.
   */
  function debtDecimals() external view returns (uint8);

  /**
   * @notice Returns the address of the underlying token used as debt in functions
   * `borrow()`, and `payback()`. Based on {IERC4626-asset}.
   *
   * @dev Requirements:
   * - Must be an ERC-20 token contract.
   * - Must not revert.
   * - Must return zero in a {YieldVault}.
   */
  function debtAsset() external view returns (address);

  /**
   * @notice Returns the amount of debt owned by `owner`.
   *
   * @param owner to check balance
   */
  function balanceOfDebt(address owner) external view returns (uint256 debt);

  /**
   * @notice Returns the amount of `debtShares` owned by `owner`.
   *
   * @param owner to check balance
   */
  function balanceOfDebtShares(address owner) external view returns (uint256 debtShares);

  /**
   * @notice Returns the total amount of the underlying debt asset
   * that is “managed” by this vault. Based on {IERC4626-totalAssets}.
   *
   * @dev Requirements:
   * - Must account for any compounding occuring from yield or interest accrual.
   * - Must be inclusive of any fees that are charged against assets in the Vault.
   * - Must not revert.
   * - Must return zero in a {YieldVault}.
   */
  function totalDebt() external view returns (uint256);

  /**
   * @notice Returns the amount of shares this vault would exchange for the amount
   * of debt assets provided. Based on {IERC4626-convertToShares}.
   *
   * @param debt to convert into `debtShares`
   *
   * @dev Requirements:
   * - 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 Must reflect the
   * “average-user’s” price-per-share, meaning what the average user Must expect to see when exchanging to and
   * from.
   */
  function convertDebtToShares(uint256 debt) external view returns (uint256 shares);

  /**
   * @notice Returns the amount of debt assets that this vault would exchange for the amount
   * of shares provided. Based on {IERC4626-convertToAssets}.
   *
   * @param shares amount to convert into `debt`
   *
   * @dev Requirements:
   * - 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 must reflect the
   * “average-user’s” price-per-share, meaning what the average user Must expect to see when exchanging to and
   * from.
   */
  function convertToDebt(uint256 shares) external view returns (uint256 debt);

  /**
   * @notice Returns the maximum amount of the debt asset that can be borrowed for the `owner`,
   * through a borrow call.
   *
   * @param owner to check
   *
   * @dev Requirements:
   * - Must return a limited value if receiver is subject to some borrow limit.
   * - Must return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be borrowed.
   * - Must not revert.
   */
  function maxBorrow(address owner) external view returns (uint256 debt);

  /**
   * @notice Returns the maximum amount of debt that can be payback by the `borrower`.
   *
   * @param owner to check
   *
   * @dev Requirements:
   * - Must not revert.
   */
  function maxPayback(address owner) external view returns (uint256 debt);

  /**
   * @notice Returns the maximum amount of debt shares that can be "minted-for-borrowing" by the `borrower`.
   *
   * @param owner to check
   *
   * @dev Requirements:
   * - Must not revert.
   */
  function maxMintDebt(address owner) external view returns (uint256 shares);

  /**
   * @notice Returns the maximum amount of debt shares that can be "burned-for-payback" by the `borrower`.
   *
   * @param owner to check
   *
   * @dev Requirements:
   * - Must not revert.
   */
  function maxBurnDebt(address owner) external view returns (uint256 shares);

  /**
   * @notice Returns the amount of `debtShares` that borrowing `debt` amount will generate.
   *
   * @param debt amount to check
   *
   * @dev Requirements:
   * - Must not revert.
   */
  function previewBorrow(uint256 debt) external view returns (uint256 shares);

  /**
   * @notice Returns the amount of debt that borrowing `debtShares` amount will generate.
   *
   * @param shares of debt to check
   *
   * @dev Requirements:
   * - Must not revert.
   */
  function previewMintDebt(uint256 shares) external view returns (uint256 debt);

  /**
   * @notice Returns the amount of `debtShares` that will be burned by paying back
   * `debt` amount.
   *
   * @param debt to check
   *
   * @dev Requirements:
   * - Must not revert.
   */
  function previewPayback(uint256 debt) external view returns (uint256 shares);

  /**
   * @notice Returns the amount of debt asset that will be pulled from user, if `debtShares` are
   * burned to payback.
   *
   * @param debt to check
   *
   * @dev Requirements:
   * - Must not revert.
   */
  function previewBurnDebt(uint256 shares) external view returns (uint256 debt);

  /**
   * @notice Perform a borrow action. Function inspired on {IERC4626-deposit}.
   *
   * @param debt amount
   * @param receiver of the `debt` amount
   * @param owner who will incur the `debt` amount
   *
   * * @dev Mints debtShares to owner by taking a loan of exact amount of underlying tokens.
   * Requirements:
   * - Must emit the Borrow event.
   * - Must revert if owner does not own sufficient collateral to back debt.
   * - Must revert if caller is not owner or permissioned operator to act on owner behalf.
   */
  function borrow(uint256 debt, address receiver, address owner) external returns (uint256 shares);

  /**
   * @notice Perform a borrow action by minting `debtShares`.
   *
   * @param shares of debt to mint
   * @param receiver of the borrowed amount
   * @param owner who will incur the `debt` and whom `debtShares` will be accounted
   *
   * * @dev Mints `debtShares` to `owner`.
   * Requirements:
   * - Must emit the Borrow event.
   * - Must revert if owner does not own sufficient collateral to back debt.
   * - Must revert if caller is not owner or permissioned operator to act on owner behalf.
   */
  function mintDebt(
    uint256 shares,
    address receiver,
    address owner
  )
    external
    returns (uint256 debt);

  /**
   * @notice Burns `debtShares` to `receiver` by paying back loan with exact amount of underlying tokens.
   *
   * @param debt amount to payback
   * @param receiver to whom debt amount is being paid back
   *
   * @dev Implementations will require pre-erc20-approval of the underlying debt token.
   * Requirements:
   * - Must emit a Payback event.
   */
  function payback(uint256 debt, address receiver) external returns (uint256 shares);

  /**
   * @notice Burns `debtShares` to `owner` by paying back loan by specifying debt shares.
   *
   * @param shares of debt to payback
   * @param owner to whom debt amount is being paid back
   *
   * @dev Implementations will require pre-erc20-approval of the underlying debt token.
   * Requirements:
   * - Must emit a Payback event.
   */
  function burnDebt(uint256 shares, address owner) external returns (uint256 debt);

  /*///////////////////
    General functions
  ///////////////////*/

  /**
   * @notice Returns the active provider of this vault.
   */
  function getProviders() external view returns (ILendingProvider[] memory);
  /**
   * @notice Returns the active provider of this vault.
   */
  function activeProvider() external view returns (ILendingProvider);

  /*/////////////////////////
     Rebalancing Function
  ////////////////////////*/

  /**
   * @notice Performs rebalancing of vault by moving funds across providers.
   *
   * @param assets amount of this vault to be rebalanced
   * @param debt amount of this vault to be rebalanced (Note: pass zero if this is a {YieldVault})
   * @param from provider
   * @param to provider
   * @param fee expected from rebalancing operation
   * @param setToAsActiveProvider boolean
   *
   * @dev Requirements:
   * - Must check providers `from` and `to` are valid.
   * - Must be called from a {RebalancerManager} contract that makes all proper checks.
   * - Must revert if caller is not an approved rebalancer.
   * - Must emit the VaultRebalance event.
   * - Must check `fee` is a reasonable amount.
   */
  function rebalance(
    uint256 assets,
    uint256 debt,
    ILendingProvider from,
    ILendingProvider to,
    uint256 fee,
    bool setToAsActiveProvider
  )
    external
    returns (bool);

  /*/////////////////////////
     Liquidation Functions
  /////////////////////////*/

  /**
   * @notice Returns the current health factor of 'owner'.
   *
   * @param owner to get health factor
   *
   * @dev Requirements:
   * - Must return type(uint254).max when 'owner' has no debt.
   * - Must revert in {YieldVault}.
   *
   * 'healthFactor' is scaled up by 1e18. A value below 1e18 means 'owner' is eligable for liquidation.
   * See factors: https://github.com/Fujicracy/CrossFuji/tree/main/packages/protocol#readme.
   */
  function getHealthFactor(address owner) external returns (uint256 healthFactor);

  /**
   * @notice Returns the liquidation close factor based on 'owner's' health factor.
   *
   * @param owner of debt position
   *
   * @dev Requirements:
   * - Must return zero if `owner` is not liquidatable.
   * - Must revert in {YieldVault}.
   */
  function getLiquidationFactor(address owner) external returns (uint256 liquidationFactor);

  /**
   * @notice Performs liquidation of an unhealthy position, meaning a 'healthFactor' below 100.
   *
   * @param owner to be liquidated
   * @param receiver of the collateral shares of liquidation
   *
   * @dev Requirements:
   * - Must revert if caller is not an approved liquidator.
   * - Must revert if 'owner' is not liquidatable.
   * - Must emit the Liquidation event.
   * - Must liquidate 50% of 'owner' debt when: 100 >= 'healthFactor' > 95.
   * - Must liquidate 100% of 'owner' debt when: 95 > 'healthFactor'.
   * - Must revert in {YieldVault}.
   *
   * WARNING! It is liquidator's responsability to check if liquidation is profitable.
   */
  function liquidate(address owner, address receiver) external returns (uint256 gainedShares);

  /*/////////////////////
     Setter functions 
  ////////////////////*/

  /**
   * @notice Sets the lists of providers of this vault.
   *
   * @param providers address array
   *
   * @dev Requirements:
   * - Must not contain zero addresses.
   */
  function setProviders(ILendingProvider[] memory providers) external;

  /**
   * @notice Sets the active provider for this vault.
   *
   * @param activeProvider address
   *
   * @dev Requirements:
   * - Must be a provider previously set by `setProviders()`.
   * - Must be called from a timelock contract.
   *
   * WARNING! Changing active provider without a `rebalance()` call
   * can result in denial of service for vault users.
   */
  function setActiveProvider(ILendingProvider activeProvider) external;

  /**
   * @notice Sets the minimum amount for: `deposit()`, `mint()` and borrow()`.
   *
   * @param amount to be as minimum.
   */
  function setMinAmount(uint256 amount) external;
}

File 9 of 25 : IVaultPermissions.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.15;

/**
 * @title IVaultPermissions
 *
 * @author Fujidao Labs
 *
 * @notice Defines the interface for a vault extended with
 * signed permit operations for `withdraw()` and `borrow()` allowance.
 */

interface IVaultPermissions {
  /**
   * @dev Emitted when `asset` withdraw allowance is set.
   *
   * @param owner who provides allowance
   * @param operator who can execute the use of the allowance
   * @param receiver who can spend the allowance
   * @param amount of allowance given
   */
  event WithdrawApproval(address indexed owner, address operator, address receiver, uint256 amount);

  /**
   * @dev Emitted when `debtAsset` borrow allowance is set.
   *
   * @param owner who provides allowance
   * @param operator who can execute the use of the allowance
   * @param receiver who can spend the allowance
   * @param amount of allowance given
   */
  event BorrowApproval(address indexed owner, address operator, address receiver, uint256 amount);

  /// @dev Based on {IERC20Permit-DOMAIN_SEPARATOR}.
  // solhint-disable-next-line func-name-mixedcase
  function DOMAIN_SEPARATOR() external returns (bytes32);

  /**
   * @notice Returns the current amount of withdraw allowance from `owner` to `receiver` that
   * can be executed by `operator`. This is similar to {IERC20-allowance} for BaseVault assets,
   * instead of token-shares.
   *
   * @param owner who provides allowance
   * @param operator who can execute the use of the allowance
   * @param receiver who can spend the allowance
   *
   * @dev Requirements:
   * - Must replace {IERC4626-allowance} in a vault implementation.
   */
  function withdrawAllowance(
    address owner,
    address operator,
    address receiver
  )
    external
    view
    returns (uint256);

  /**
   * @notice Returns the current amount of borrow allowance from `owner` to `receiver` that
   * can be executed by `operator`. This is similar to {IERC20-allowance} for
   * BaseVault-debtAsset.
   *
   * @param owner who provides allowance
   * @param operator who can execute the use of the allowance
   * @param receiver who can spend the allowance
   */
  function borrowAllowance(
    address owner,
    address operator,
    address receiver
  )
    external
    view
    returns (uint256);

  /**
   * @dev Atomically increases the `withdrawAllowance` granted to `receiver` and
   * executable by `operator` by the caller. Based on OZ {ERC20-increaseAllowance} for assets.
   *
   * @param operator who can execute the use of the allowance
   * @param receiver who can spend the allowance
   * @param byAmount to increase withdraw allowance
   *
   * @dev Requirements:
   * - Must emit a {WithdrawApproval} event indicating the updated withdraw allowance.
   * - Must check `operator` and `receiver are not zero address.
   */
  function increaseWithdrawAllowance(
    address operator,
    address receiver,
    uint256 byAmount
  )
    external
    returns (bool);

  /**
   * @dev Atomically decreases the `withdrawAllowance` granted to `receiver` and
   * executable by `operator` by the caller. Based on OZ {ERC20-decreaseAllowance} for assets.
   *
   * @param operator who can execute the use of the allowance
   * @param receiver who can spend the allowance
   * @param byAmount to decrease withdraw allowance
   *
   * @dev Requirements:
   * - Must emit a {WithdrawApproval} event indicating the updated withdraw allowance.
   * - Must check `operator` and `receiver` are not zero address.
   * - Must check `operator` and `receiver` have `borrowAllowance` of at least `byAmount`.
   *
   */
  function decreaseWithdrawAllowance(
    address operator,
    address receiver,
    uint256 byAmount
  )
    external
    returns (bool);

  /**
   * @dev Atomically increases the `borrowAllowance` granted to `receiver` and
   * executable by `operator` by the caller. Based on OZ {ERC20-increaseAllowance}
   * for `debtAsset`.
   *
   * @param operator address who can execute the use of the allowance
   * @param receiver address who can spend the allowance
   * @param byAmount to increase borrow allowance
   *
   * @dev Requirements:
   * - Must emit a {BorrowApproval} event indicating the updated borrow allowance.
   * - Must check `operator` and `receiver` are not zero address.
   */
  function increaseBorrowAllowance(
    address operator,
    address receiver,
    uint256 byAmount
  )
    external
    returns (bool);

  /**
   * @dev Atomically decrease the `borrowAllowance` granted to `receiver` and
   * executable by `operator` by the caller. Based on OZ {ERC20-decreaseAllowance}
   * for `debtAsset`.
   *
   * @param operator who can execute the use of the allowance
   * @param receiver who can spend the allowance
   * @param byAmount to decrease borrow allowance
   *
   * Requirements:
   * - Must emit a {BorrowApproval} event indicating the updated borrow allowance.
   * - Must check `operator` and `receiver` are not the zero address.
   * - Must check `operator` and `receiver` have `borrowAllowance` of at least `byAmount`.
   */
  function decreaseBorrowAllowance(
    address operator,
    address receiver,
    uint256 byAmount
  )
    external
    returns (bool);

  /**
   * @notice Returns the curent used nonces for permits of `owner`.
   * Based on OZ {IERC20Permit-nonces}.
   *
   * @param owner address to check nonces
   */
  function nonces(address owner) external view returns (uint256);

  /**
   * @notice Sets `amount` as the `withdrawAllowance` of `receiver` executable by
   * caller over `owner`'s tokens, given the `owner`'s signed approval.
   * Inspired by {IERC20Permit-permit} for assets.
   *
   * @param owner providing allowance
   * @param receiver who can spend the allowance
   * @param amount of allowance
   * @param deadline timestamp limit for the execution of signed permit
   * @param actionArgsHash keccak256 of the abi.encoded(args,actions) to be performed in {BaseRouter._internalBundle}
   * @param v signature value
   * @param r signature value
   * @param s signature value
   *
   * @dev Requirements:
   * - Must check `deadline` is a timestamp in the future.
   * - Must check `receiver` is a non-zero address.
   * - Must check that `v`, `r` and `s` are valid `secp256k1` signature for `owner`
   *   over EIP712-formatted function arguments.
   * - Must check the signature used `owner`'s current nonce (see {nonces}).
   * - Must emits an {AssetsApproval} event.
   */
  function permitWithdraw(
    address owner,
    address receiver,
    uint256 amount,
    uint256 deadline,
    bytes32 actionArgsHash,
    uint8 v,
    bytes32 r,
    bytes32 s
  )
    external;

  /**
   * @notice Sets `amount` as the `borrowAllowance` of `receiver` executable by caller over
   * `owner`'s borrowing powwer, given the `owner`'s signed approval.
   * Inspired by {IERC20Permit-permit} for debt.
   *
   * @param owner address providing allowance
   * @param receiver address who can spend the allowance
   * @param amount of allowance
   * @param deadline timestamp limit for the execution of signed permit
   * @param actionArgsHash keccak256 of the abi.encoded(args,actions) to be performed in {BaseRouter._internalBundle}
   * @param v signature value
   * @param r signature value
   * @param s signature value
   *
   * @dev Requirements:
   * - Must emit a {BorrowApproval} event.
   * - Must be implemented in a {BorrowingVault}.
   * - Must check `deadline` is a timestamp in the future.
   * - Must check `receiver` is a non-zero address.
   * - Must check that `v`, `r` and `s` are valid `secp256k1` signature for `owner`.
   *   over EIP712-formatted function arguments.
   * - Must check the signature used `owner`'s current nonce (see {nonces}).
   */
  function permitBorrow(
    address owner,
    address receiver,
    uint256 amount,
    uint256 deadline,
    bytes32 actionArgsHash,
    uint8 v,
    bytes32 r,
    bytes32 s
  )
    external;
}

File 10 of 25 : IChief.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.15;

/**
 * @title IChief
 *
 * @author Fujidao Labs
 *
 * @notice Defines interface for {Chief} access control operations.
 */

import {IAccessControl} from "openzeppelin-contracts/contracts/access/IAccessControl.sol";

interface IChief is IAccessControl {
  /// @notice Returns the timelock address of the FujiV2 system.
  function timelock() external view returns (address);

  /// @notice Returns the address mapper contract address of the FujiV2 system.
  function addrMapper() external view returns (address);

  /**
   * @notice Returns true if `vault` is active.
   *
   * @param vault to check status
   */
  function isVaultActive(address vault) external view returns (bool);

  /**
   * @notice Returns true if `flasher` is an allowed {IFlasher}.
   *
   * @param flasher address to check
   */
  function allowedFlasher(address flasher) external view returns (bool);

  /**
   * @notice Returns true if `swapper` is an allowed {ISwapper}.
   *
   * @param swapper address to check
   */
  function allowedSwapper(address swapper) external view returns (bool);
}

File 11 of 25 : IRouter.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.15;

/**
 * @title Router Interface
 *
 * @author Fujidao Labs
 *
 * @notice Define the interface for router operations.
 */

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

interface IRouter {
  /// @dev List of actions allowed to be executed by the router.
  enum Action {
    Deposit,
    Withdraw,
    Borrow,
    Payback,
    Flashloan,
    Swap,
    PermitWithdraw,
    PermitBorrow,
    XTransfer,
    XTransferWithCall,
    DepositETH,
    WithdrawETH
  }

  /**
   * @notice An entry-point function that executes encoded commands along with provided inputs.
   *
   * @param actions an array of actions that will be executed in a row
   * @param args an array of encoded inputs needed to execute each action
   */
  function xBundle(Action[] memory actions, bytes[] memory args) external payable;

  /**
   * @notice Sweeps accidental ERC-20 transfers to this contract or stuck funds due to failed
   * cross-chain calls (cf. ConnextRouter).
   *
   * @param token the address of the ERC-20 token to sweep
   * @param receiver the address that will receive the swept funds
   */
  function sweepToken(ERC20 token, address receiver) external;

  /**
   * @notice Sweeps accidental ETH transfers to this contract.
   *
   * @param receiver the address that will receive the swept funds
   */
  function sweepETH(address receiver) external;
}

File 12 of 25 : IFlasher.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.15;

/**
 * @title IFlasher
 * @author Fujidao Labs
 * @notice Defines the interface for all flashloan providers.
 */

interface IFlasher {
  /**
   * @notice Initiates a flashloan a this provider.
   * @param asset address to be flashloaned.
   * @param amount of `asset` to be flashloaned.
   * @param requestor address to which flashloan will be facilitated.
   * @param requestorCalldata encoded args with selector that will be OPCODE-CALL'ed to `requestor`.
   * @dev To encode `params` see examples:
   * • solidity:
   *   > abi.encodeWithSelector(contract.transferFrom.selector, from, to, amount);
   * • ethersJS:
   *   > contract.interface.encodeFunctionData("transferFrom", [from, to, amount]);
   * • foundry cast:
   *   > cast calldata "transferFrom(address,address,uint256)" from, to, amount
   *
   * Requirements:
   * - MUST implement `_checkAndSetEntryPoint()`
   */
  function initiateFlashloan(
    address asset,
    uint256 amount,
    address requestor,
    bytes memory requestorCalldata
  )
    external;

  /**
   * @notice Returns the address from which flashloan for `asset` is sourced.
   * @param asset intended to be flashloaned.
   * @dev Override at flashloan provider implementation as required.
   * Some protocol implementations source flashloans from different contracts
   * depending on `asset`.
   */
  function getFlashloanSourceAddr(address asset) external view returns (address callAddr);

  /**
   * @notice Returns the expected flashloan fee for `amount`
   * of this flashloan provider.
   * @param asset to be flashloaned
   * @param amount of flashloan
   */
  function computeFlashloanFee(address asset, uint256 amount) external view returns (uint256 fee);
}

File 13 of 25 : LibBytes.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.15;

/*
 * @title LibBytes

 * @author Gonçalo Sá <[email protected]>
 *
 * @notice Utility library for ethereum contracts written in Solidity.
 * The library lets you concatenate, slice and type cast bytes arrays 
 * both in memory and storage. Taken from:
 * https://github.com/GNSPS/solidity-bytes-utils/blob/master/contracts/BytesLib.sol.
 */
library LibBytes {
  function concat(
    bytes memory _preBytes,
    bytes memory _postBytes
  )
    internal
    pure
    returns (bytes memory)
  {
    bytes memory tempBytes;

    assembly {
      /**
       * @dev Get a location of some free memory and store it in tempBytes as
       * Solidity does for memory variables.
       */
      tempBytes := mload(0x40)

      /**
       * @dev Store the length of the first bytes array at the beginning of
       * the memory for tempBytes.
       */
      let length := mload(_preBytes)
      mstore(tempBytes, length)

      /**
       * @dev Maintain a memory counter for the current write location in the
       * temp bytes array by adding the 32 bytes for the array length to
       * the starting location.
       */
      let mc := add(tempBytes, 0x20)
      // Stop copying when the memory counter reaches the length of the first bytes array.
      let end := add(mc, length)

      for {
        // Initialize a copy counter to the start of the _preBytes data, 32 bytes into its memory.
        let cc := add(_preBytes, 0x20)
      } lt(mc, end) {
        // Increase both counters by 32 bytes each iteration.
        mc := add(mc, 0x20)
        cc := add(cc, 0x20)
      } {
        // Write the _preBytes data into the tempBytes memory 32 bytes at a time.
        mstore(mc, mload(cc))
      }

      /**
       * @dev Add the length of _postBytes to the current length of tempBytes
       * and store it as the new length in the first 32 bytes of the
       * tempBytes memory.
       */
      length := mload(_postBytes)
      mstore(tempBytes, add(length, mload(tempBytes)))

      // Move the memory counter back from a multiple of 0x20 to the  actual end of the _preBytes data.
      mc := end
      // Stop copying when the memory counter reaches the new combined length of the arrays.
      end := add(mc, length)

      for { let cc := add(_postBytes, 0x20) } lt(mc, end) {
        mc := add(mc, 0x20)
        cc := add(cc, 0x20)
      } { mstore(mc, mload(cc)) }

      /**
       * @dev Update the free-memory pointer by padding our last write location
       * to 32 bytes: add 31 bytes to the end of tempBytes to move to the
       * next 32 byte block, then round down to the nearest multiple of
       * 32. If the sum of the length of the two arrays is zero then add
       * one before rounding down to leave a blank 32 bytes (the length block with 0).
       */
      mstore(
        0x40,
        and(
          add(add(end, iszero(add(length, mload(_preBytes)))), 31),
          // Round down to the nearest 32 bytes.
          not(31)
        )
      )
    }

    return tempBytes;
  }

  function concatStorage(bytes storage _preBytes, bytes memory _postBytes) internal {
    assembly {
      /**
       * @dev Read the first 32 bytes of _preBytes storage, which is the length
       * of the array. (We don't need to use the offset into the slot
       * because arrays use the entire slot.)
       */
      let fslot := sload(_preBytes.slot)
      /**
       * @dev Arrays of 31 bytes or less have an even value in their slot,
       * while longer arrays have an odd value. The actual length is
       * the slot divided by two for odd values, and the lowest order
       * byte divided by two for even values.
       * If the slot is even, bitwise and the slot with 255 and divide by
       * two to get the length. If the slot is odd, bitwise and the slot
       * with -1 and divide by two.
       */
      let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)
      let mlength := mload(_postBytes)
      let newlength := add(slength, mlength)

      /**
       * @dev // slength can contain both the length and contents of the array
       * if length < 32 bytes so let's prepare for that
       * v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage
       */
      switch add(lt(slength, 32), lt(newlength, 32))
      case 2 {
        /**
         * @dev Since the new array still fits in the slot, we just need to
         * update the contents of the slot.
         * uint256(bytes_storage) = uint256(bytes_storage) + uint256(bytes_memory) + new_length
         */
        sstore(
          _preBytes.slot,
          // All the modifications to the slot are inside this next block
          add(
            // we can just add to the slot contents because the bytes we want to change are the LSBs
            fslot,
            add(
              mul(
                div(
                  // load the bytes from memory.
                  mload(add(_postBytes, 0x20)),
                  // Zero all bytes to the right.
                  exp(0x100, sub(32, mlength))
                ),
                // Now shift left the number of bytes to leave space for the length in the slot.
                exp(0x100, sub(32, newlength))
              ),
              // Increase length by the double of the memory bytes length.
              mul(mlength, 2)
            )
          )
        )
      }
      case 1 {
        /**
         * @dev The stored value fits in the slot, but the combined value
         * will exceed it. Get the keccak hash to get the contents of the array.
         */
        mstore(0x0, _preBytes.slot)
        let sc := add(keccak256(0x0, 0x20), div(slength, 32))

        // Save new length.
        sstore(_preBytes.slot, add(mul(newlength, 2), 1))

        /**
         * @dev The contents of the _postBytes array start 32 bytes into
         * the structure. Our first read should obtain the `submod`
         * bytes that can fit into the unused space in the last word
         * of the stored array. To get this, we read 32 bytes starting
         * from `submod`, so the data we read overlaps with the array
         * contents by `submod` bytes. Masking the lowest-order
         * `submod` bytes allows us to add that value directly to the
         * stored value.
         */
        let submod := sub(32, slength)
        let mc := add(_postBytes, submod)
        let end := add(_postBytes, mlength)
        let mask := sub(exp(0x100, submod), 1)

        sstore(
          sc,
          add(
            and(fslot, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00),
            and(mload(mc), mask)
          )
        )

        for {
          mc := add(mc, 0x20)
          sc := add(sc, 1)
        } lt(mc, end) {
          sc := add(sc, 1)
          mc := add(mc, 0x20)
        } { sstore(sc, mload(mc)) }

        mask := exp(0x100, sub(mc, end))

        sstore(sc, mul(div(mload(mc), mask), mask))
      }
      default {
        // Get the keccak hash to get the contents of the array.
        mstore(0x0, _preBytes.slot)
        // Start copying to the last used word of the stored array.
        let sc := add(keccak256(0x0, 0x20), div(slength, 32))

        // Save new length.
        sstore(_preBytes.slot, add(mul(newlength, 2), 1))

        // Copy over the first `submod` bytes of the new data as in case 1 above.
        let slengthmod := mod(slength, 32)
        let mlengthmod := mod(mlength, 32)
        let submod := sub(32, slengthmod)
        let mc := add(_postBytes, submod)
        let end := add(_postBytes, mlength)
        let mask := sub(exp(0x100, submod), 1)

        sstore(sc, add(sload(sc), and(mload(mc), mask)))

        for {
          sc := add(sc, 1)
          mc := add(mc, 0x20)
        } lt(mc, end) {
          sc := add(sc, 1)
          mc := add(mc, 0x20)
        } { sstore(sc, mload(mc)) }

        mask := exp(0x100, sub(mc, end))

        sstore(sc, mul(div(mload(mc), mask), mask))
      }
    }
  }

  function slice(
    bytes memory _bytes,
    uint256 _start,
    uint256 _length
  )
    internal
    pure
    returns (bytes memory)
  {
    require(_length + 31 >= _length, "slice_overflow");
    require(_bytes.length >= _start + _length, "slice_outOfBounds");

    bytes memory tempBytes;

    assembly {
      switch iszero(_length)
      case 0 {
        // Get a location of some free memory and store it in tempBytes as Solidity does for memory variables.
        tempBytes := mload(0x40)

        /**
         * @dev The first word of the slice result is potentially a partial
         * word read from the original array. To read it, we calculate
         * the length of that partial word and start copying that many
         * bytes into the array. The first word we copy will start with
         * data we don't care about, but the last `lengthmod` bytes will
         * land at the beginning of the contents of the new array. When
         * we're done copying, we overwrite the full first word with
         * the actual length of the slice.
         */
        let lengthmod := and(_length, 31)

        /**
         * @dev The multiplication in the next line is necessary
         * because when slicing multiples of 32 bytes (lengthmod == 0)
         * the following copy loop was copying the origin's length
         * and then ending prematurely not copying everything it should.
         */
        let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))
        let end := add(mc, _length)

        for {
          // The multiplication in the next line has the same exact purpose as the one above.
          let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start)
        } lt(mc, end) {
          mc := add(mc, 0x20)
          cc := add(cc, 0x20)
        } { mstore(mc, mload(cc)) }

        mstore(tempBytes, _length)

        // Update free-memory pointer allocating the array padded to 32 bytes like the compiler does now.
        mstore(0x40, and(add(mc, 31), not(31)))
      }
      // If we want a zero-length slice let's just return a zero-length array.
      default {
        tempBytes := mload(0x40)
        // Zero out the 32 bytes slice we are about to return we need to do it because Solidity does not garbage collect
        mstore(tempBytes, 0)

        mstore(0x40, add(tempBytes, 0x20))
      }
    }

    return tempBytes;
  }

  function toAddress(bytes memory _bytes, uint256 _start) internal pure returns (address) {
    require(_bytes.length >= _start + 20, "toAddress_outOfBounds");
    address tempAddress;

    assembly {
      tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000)
    }

    return tempAddress;
  }

  function toUint8(bytes memory _bytes, uint256 _start) internal pure returns (uint8) {
    require(_bytes.length >= _start + 1, "toUint8_outOfBounds");
    uint8 tempUint;

    assembly {
      tempUint := mload(add(add(_bytes, 0x1), _start))
    }

    return tempUint;
  }

  function toUint16(bytes memory _bytes, uint256 _start) internal pure returns (uint16) {
    require(_bytes.length >= _start + 2, "toUint16_outOfBounds");
    uint16 tempUint;

    assembly {
      tempUint := mload(add(add(_bytes, 0x2), _start))
    }

    return tempUint;
  }

  function toUint32(bytes memory _bytes, uint256 _start) internal pure returns (uint32) {
    require(_bytes.length >= _start + 4, "toUint32_outOfBounds");
    uint32 tempUint;

    assembly {
      tempUint := mload(add(add(_bytes, 0x4), _start))
    }

    return tempUint;
  }

  function toUint64(bytes memory _bytes, uint256 _start) internal pure returns (uint64) {
    require(_bytes.length >= _start + 8, "toUint64_outOfBounds");
    uint64 tempUint;

    assembly {
      tempUint := mload(add(add(_bytes, 0x8), _start))
    }

    return tempUint;
  }

  function toUint96(bytes memory _bytes, uint256 _start) internal pure returns (uint96) {
    require(_bytes.length >= _start + 12, "toUint96_outOfBounds");
    uint96 tempUint;

    assembly {
      tempUint := mload(add(add(_bytes, 0xc), _start))
    }

    return tempUint;
  }

  function toUint128(bytes memory _bytes, uint256 _start) internal pure returns (uint128) {
    require(_bytes.length >= _start + 16, "toUint128_outOfBounds");
    uint128 tempUint;

    assembly {
      tempUint := mload(add(add(_bytes, 0x10), _start))
    }

    return tempUint;
  }

  function toUint256(bytes memory _bytes, uint256 _start) internal pure returns (uint256) {
    require(_bytes.length >= _start + 32, "toUint256_outOfBounds");
    uint256 tempUint;

    assembly {
      tempUint := mload(add(add(_bytes, 0x20), _start))
    }

    return tempUint;
  }

  function toBytes32(bytes memory _bytes, uint256 _start) internal pure returns (bytes32) {
    require(_bytes.length >= _start + 32, "toBytes32_outOfBounds");
    bytes32 tempBytes32;

    assembly {
      tempBytes32 := mload(add(add(_bytes, 0x20), _start))
    }

    return tempBytes32;
  }

  function equal(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bool) {
    bool success = true;

    assembly {
      let length := mload(_preBytes)

      // If lengths don't match the arrays are not equal
      switch eq(length, mload(_postBytes))
      case 1 {
        /**
         * @dev cb is a circuit breaker in the for loop since there's
         * no said feature for inline assembly loops
         * cb = 1 - don't breaker
         * cb = 0 - break
         */
        let cb := 1

        let mc := add(_preBytes, 0x20)
        let end := add(mc, length)

        for { let cc := add(_postBytes, 0x20) }
        // The next line is the loop condition: while(uint256(mc < end) + cb == 2).
        eq(add(lt(mc, end), cb), 2) {
          mc := add(mc, 0x20)
          cc := add(cc, 0x20)
        } {
          // If any of these checks fails then arrays are not equal.
          if iszero(eq(mload(mc), mload(cc))) {
            // Unsuccess:
            success := 0
            cb := 0
          }
        }
      }
      default {
        // Unsuccess:
        success := 0
      }
    }

    return success;
  }

  function equalStorage(
    bytes storage _preBytes,
    bytes memory _postBytes
  )
    internal
    view
    returns (bool)
  {
    bool success = true;

    assembly {
      // We know _preBytes_offset is 0.
      let fslot := sload(_preBytes.slot)
      // Decode the length of the stored array like in concatStorage().
      let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)
      let mlength := mload(_postBytes)

      // If lengths don't match the arrays are not equal.
      switch eq(slength, mlength)
      case 1 {
        /**
         * @dev Slength can contain both the length and contents of the array
         * if length < 32 bytes so let's prepare for that
         * v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage
         */
        if iszero(iszero(slength)) {
          switch lt(slength, 32)
          case 1 {
            // Blank the last byte which is the length.
            fslot := mul(div(fslot, 0x100), 0x100)

            if iszero(eq(fslot, mload(add(_postBytes, 0x20)))) {
              // Unsuccess:
              success := 0
            }
          }
          default {
            /**
             * @dev cb is a circuit breaker in the for loop since there's
             * no said feature for inline assembly loops
             * cb = 1 - don't breaker
             * cb = 0 - break
             */
            let cb := 1

            // Get the keccak hash to get the contents of the array.
            mstore(0x0, _preBytes.slot)
            let sc := keccak256(0x0, 0x20)

            let mc := add(_postBytes, 0x20)
            let end := add(mc, mlength)

            // The next line is the loop condition: while(uint256(mc < end) + cb == 2)
            for {} eq(add(lt(mc, end), cb), 2) {
              sc := add(sc, 1)
              mc := add(mc, 0x20)
            } {
              if iszero(eq(sload(sc), mload(mc))) {
                // Unsuccess:
                success := 0
                cb := 0
              }
            }
          }
        }
      }
      default {
        // Unsuccess:
        success := 0
      }
    }

    return success;
  }
}

File 14 of 25 : IERC20Permit.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Permit.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

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

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

File 15 of 25 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     *
     * Furthermore, `isContract` will also return true if the target contract within
     * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
     * which only has an effect at the end of a transaction.
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

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

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

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

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
     *
     * _Available since v4.8._
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                // only check isContract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                require(isContract(target), "Address: call to non-contract");
            }
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

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

File 16 of 25 : ISwapper.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.15;

/**
 * @title ISwapper
 *
 * @author Fujidao Labs
 *
 * @notice  Defines the interface for routers to perform token swaps with DEX protocols.
 *
 * @dev Implementation inheriting this interface should be permisionless.
 */

interface ISwapper {
  /**
   * @notice Swap tokens at exchange.
   *
   * @param assetIn address of the ERC-20 token to swap from
   * @param assetOut address of the ERC-20 token to swap to
   * @param amountIn that will be pulled from msg.sender
   * @param amountOut of `assetOut` expected after the swap
   * @param receiver of the `amountOut` tokens
   * @param sweeper who receives the leftovers `assetIn` tokens after swap
   * @param minSweepOut  amount of `assetIn` leftover expected after swap
   *
   * @dev Slippage is controlled through `minSweepOut`. If `minSweepOut` is 0,
   * the slippage check gets skipped.
   */
  function swap(
    address assetIn,
    address assetOut,
    uint256 amountIn,
    uint256 amountOut,
    address receiver,
    address sweeper,
    uint256 minSweepOut
  )
    external;

  /**
   * @notice Estimate the amount of `assetIn` required for `swap()`.
   *
   * @param assetIn address of the ERC-20 token to swap from
   * @param assetOut address of the ERC-20 token to swap to
   * @param amountOut expected amount of `assetOut` after the swap
   */
  function getAmountIn(
    address assetIn,
    address assetOut,
    uint256 amountOut
  )
    external
    view
    returns (uint256 amountIn);

  /**
   * @notice Estimate the amount of `assetOut` received after swap
   *
   * @param assetIn address of the ERC-20 token to swap from
   * @param assetOut address of the ERC-20 token to swap to
   * @param amountIn of `assetIn` to perform swap
   */
  function getAmountOut(
    address assetIn,
    address assetOut,
    uint256 amountIn
  )
    external
    view
    returns (uint256 amountOut);
}

File 17 of 25 : ERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/ERC20.sol)

pragma solidity ^0.8.0;

import "./IERC20.sol";
import "./extensions/IERC20Metadata.sol";
import "../../utils/Context.sol";

/**
 * @dev Implementation of the {IERC20} interface.
 *
 * This implementation is agnostic to the way tokens are created. This means
 * that a supply mechanism has to be added in a derived contract using {_mint}.
 * For a generic mechanism see {ERC20PresetMinterPauser}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * The default value of {decimals} is 18. To change this, you should override
 * this function so it returns a different value.
 *
 * We have followed general OpenZeppelin Contracts guidelines: functions revert
 * instead returning `false` on failure. This behavior is nonetheless
 * conventional and does not conflict with the expectations of ERC20
 * applications.
 *
 * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
 * This allows applications to reconstruct the allowance for all accounts just
 * by listening to said events. Other implementations of the EIP may not emit
 * these events, as it isn't required by the specification.
 *
 * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
 * functions have been added to mitigate the well-known issues around setting
 * allowances. See {IERC20-approve}.
 */
contract ERC20 is Context, IERC20, IERC20Metadata {
    mapping(address => uint256) private _balances;

    mapping(address => mapping(address => uint256)) private _allowances;

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;

    /**
     * @dev Sets the values for {name} and {symbol}.
     *
     * All two of these values are immutable: they can only be set once during
     * construction.
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view virtual override returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }

    /**
     * @dev Returns the number of decimals used to get its user representation.
     * For example, if `decimals` equals `2`, a balance of `505` tokens should
     * be displayed to a user as `5.05` (`505 / 10 ** 2`).
     *
     * Tokens usually opt for a value of 18, imitating the relationship between
     * Ether and Wei. This is the default value returned by this function, unless
     * it's overridden.
     *
     * NOTE: This information is only used for _display_ purposes: it in
     * no way affects any of the arithmetic of the contract, including
     * {IERC20-balanceOf} and {IERC20-transfer}.
     */
    function decimals() public view virtual override returns (uint8) {
        return 18;
    }

    /**
     * @dev See {IERC20-totalSupply}.
     */
    function totalSupply() public view virtual override returns (uint256) {
        return _totalSupply;
    }

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view virtual override returns (uint256) {
        return _balances[account];
    }

    /**
     * @dev See {IERC20-transfer}.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - the caller must have a balance of at least `amount`.
     */
    function transfer(address to, uint256 amount) public virtual override returns (bool) {
        address owner = _msgSender();
        _transfer(owner, to, amount);
        return true;
    }

    /**
     * @dev See {IERC20-allowance}.
     */
    function allowance(address owner, address spender) public view virtual override returns (uint256) {
        return _allowances[owner][spender];
    }

    /**
     * @dev See {IERC20-approve}.
     *
     * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
     * `transferFrom`. This is semantically equivalent to an infinite approval.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 amount) public virtual override returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, amount);
        return true;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Emits an {Approval} event indicating the updated allowance. This is not
     * required by the EIP. See the note at the beginning of {ERC20}.
     *
     * NOTE: Does not update the allowance if the current allowance
     * is the maximum `uint256`.
     *
     * Requirements:
     *
     * - `from` and `to` cannot be the zero address.
     * - `from` must have a balance of at least `amount`.
     * - the caller must have allowance for ``from``'s tokens of at least
     * `amount`.
     */
    function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) {
        address spender = _msgSender();
        _spendAllowance(from, spender, amount);
        _transfer(from, to, amount);
        return true;
    }

    /**
     * @dev Atomically increases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, allowance(owner, spender) + addedValue);
        return true;
    }

    /**
     * @dev Atomically decreases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `spender` must have allowance for the caller of at least
     * `subtractedValue`.
     */
    function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
        address owner = _msgSender();
        uint256 currentAllowance = allowance(owner, spender);
        require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
        unchecked {
            _approve(owner, spender, currentAllowance - subtractedValue);
        }

        return true;
    }

    /**
     * @dev Moves `amount` of tokens from `from` to `to`.
     *
     * This internal function is equivalent to {transfer}, and can be used to
     * e.g. implement automatic token fees, slashing mechanisms, etc.
     *
     * Emits a {Transfer} event.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `from` must have a balance of at least `amount`.
     */
    function _transfer(address from, address to, uint256 amount) internal virtual {
        require(from != address(0), "ERC20: transfer from the zero address");
        require(to != address(0), "ERC20: transfer to the zero address");

        _beforeTokenTransfer(from, to, amount);

        uint256 fromBalance = _balances[from];
        require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
        unchecked {
            _balances[from] = fromBalance - amount;
            // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by
            // decrementing then incrementing.
            _balances[to] += amount;
        }

        emit Transfer(from, to, amount);

        _afterTokenTransfer(from, to, amount);
    }

    /** @dev Creates `amount` tokens and assigns them to `account`, increasing
     * the total supply.
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     */
    function _mint(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: mint to the zero address");

        _beforeTokenTransfer(address(0), account, amount);

        _totalSupply += amount;
        unchecked {
            // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.
            _balances[account] += amount;
        }
        emit Transfer(address(0), account, amount);

        _afterTokenTransfer(address(0), account, amount);
    }

    /**
     * @dev Destroys `amount` tokens from `account`, reducing the
     * total supply.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     * - `account` must have at least `amount` tokens.
     */
    function _burn(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: burn from the zero address");

        _beforeTokenTransfer(account, address(0), amount);

        uint256 accountBalance = _balances[account];
        require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
        unchecked {
            _balances[account] = accountBalance - amount;
            // Overflow not possible: amount <= accountBalance <= totalSupply.
            _totalSupply -= amount;
        }

        emit Transfer(account, address(0), amount);

        _afterTokenTransfer(account, address(0), amount);
    }

    /**
     * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
     *
     * This internal function is equivalent to `approve`, and can be used to
     * e.g. set automatic allowances for certain subsystems, etc.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     */
    function _approve(address owner, address spender, uint256 amount) internal virtual {
        require(owner != address(0), "ERC20: approve from the zero address");
        require(spender != address(0), "ERC20: approve to the zero address");

        _allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }

    /**
     * @dev Updates `owner` s allowance for `spender` based on spent `amount`.
     *
     * Does not update the allowance amount in case of infinite allowance.
     * Revert if not enough allowance is available.
     *
     * Might emit an {Approval} event.
     */
    function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {
        uint256 currentAllowance = allowance(owner, spender);
        if (currentAllowance != type(uint256).max) {
            require(currentAllowance >= amount, "ERC20: insufficient allowance");
            unchecked {
                _approve(owner, spender, currentAllowance - amount);
            }
        }
    }

    /**
     * @dev Hook that is called before any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * will be transferred to `to`.
     * - when `from` is zero, `amount` tokens will be minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {}

    /**
     * @dev Hook that is called after any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * has been transferred to `to`.
     * - when `from` is zero, `amount` tokens have been minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens have been burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {}
}

File 18 of 25 : SystemAccessControl.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.15;

/**
 * @title SystemAccessControl
 *
 * @author Fujidao Labs
 *
 * @notice Abstract contract that should be inherited by contract implementations that
 * call the {Chief} contract for access control checks.
 */

import {IChief} from "../interfaces/IChief.sol";
import {CoreRoles} from "./CoreRoles.sol";

contract SystemAccessControl is CoreRoles {
  /// @dev Custom Errors
  error SystemAccessControl__hasRole_missingRole(address caller, bytes32 role);
  error SystemAccessControl__onlyTimelock_callerIsNotTimelock();
  error SystemAccessControl__onlyHouseKeeper_notHouseKeeper();

  IChief public immutable chief;

  /**
   * @dev Modifier that checks `caller` has `role`.
   */
  modifier hasRole(address caller, bytes32 role) {
    if (!chief.hasRole(role, caller)) {
      revert SystemAccessControl__hasRole_missingRole(caller, role);
    }
    _;
  }

  /**
   * @dev Modifier that checks `msg.sender` has HOUSE_KEEPER_ROLE.
   */
  modifier onlyHouseKeeper() {
    if (!chief.hasRole(HOUSE_KEEPER_ROLE, msg.sender)) {
      revert SystemAccessControl__onlyHouseKeeper_notHouseKeeper();
    }
    _;
  }

  /**
   * @dev Modifier that checks `msg.sender` is the defined `timelock` in {Chief}
   * contract.
   */
  modifier onlyTimelock() {
    if (msg.sender != chief.timelock()) {
      revert SystemAccessControl__onlyTimelock_callerIsNotTimelock();
    }
    _;
  }

  /**
   * @notice Abstract constructor of a new {SystemAccessControl}.
   *
   * @param chief_ address
   *
   * @dev Requirements:
   * - Must pass non-zero {Chief} address, that could be checked at child contract.
   */
  constructor(address chief_) {
    chief = IChief(chief_);
  }
}

File 19 of 25 : IERC4626.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (interfaces/IERC4626.sol)

pragma solidity ^0.8.0;

import "../token/ERC20/IERC20.sol";
import "../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].
 *
 * _Available since v4.7._
 */
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 20 of 25 : ILendingProvider.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.15;

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

/**
 * @title ILendingProvider
 *
 * @author Fujidao Labs
 *
 * @notice  Defines the interface for core engine to perform operations at lending providers.
 *
 * @dev Functions are intended to be called in the context of a Vault via delegateCall,
 * except indicated.
 */

interface ILendingProvider {
  function providerName() external view returns (string memory);
  /**
   * @notice Returns the operator address that requires ERC20-approval for vault operations.
   *
   * @param keyAsset address to inquiry operator
   * @param asset address of the calling vault
   * @param debtAsset address of the calling vault. Note: if {YieldVault} this will be address(0).
   *
   * @dev Provider implementations may or not require all 3 inputs.
   */
  function approvedOperator(
    address keyAsset,
    address asset,
    address debtAsset
  )
    external
    view
    returns (address operator);

  /**
   * @notice Performs deposit operation at lending provider on behalf vault.
   *
   * @param amount amount to deposit
   * @param vault IVault calling this function
   *
   * @dev Requirements:
   * - This function should be delegate called in the context of a `vault`.
   */
  function deposit(uint256 amount, IVault vault) external returns (bool success);

  /**
   * @notice Performs borrow operation at lending provider on behalf vault.
   *
   * @param amount amount to borrow
   * @param vault IVault calling this function
   *
   * @dev Requirements:
   * - This function should be delegate called in the context of a `vault`.
   */
  function borrow(uint256 amount, IVault vault) external returns (bool success);

  /**
   * @notice Performs withdraw operation at lending provider on behalf vault.
   * @param amount amount to withdraw
   * @param vault IVault calling this function.
   *
   * @dev Requirements:
   * - This function should be delegate called in the context of a `vault`.
   */
  function withdraw(uint256 amount, IVault vault) external returns (bool success);

  /**
   *
   * @notice Performs payback operation at lending provider on behalf vault.
   *
   * @param amount amount to payback
   * @param vault IVault calling this function.
   *
   * @dev Requirements:
   * - This function should be delegate called in the context of a `vault`.
   * - Check there is erc20-approval to `approvedOperator` by the `vault` prior to call.
   */
  function payback(uint256 amount, IVault vault) external returns (bool success);

  /**
   * @notice Returns DEPOSIT balance of 'user' at lending provider.
   *
   * @param user address whom balance is needed
   * @param vault IVault required by some specific providers with multi-markets, otherwise pass address(0).
   *
   * @dev Requirements:
   * - Must not require Vault context.
   */
  function getDepositBalance(address user, IVault vault) external view returns (uint256 balance);

  /**
   * @notice Returns BORROW balance of 'user' at lending provider.
   *
   * @param user address whom balance is needed
   * @param vault IVault required by some specific providers with multi-markets, otherwise pass address(0).
   *
   * @dev Requirements:
   * - Must not require Vault context.
   */
  function getBorrowBalance(address user, IVault vault) external view returns (uint256 balance);

  /**
   * @notice Returns the latest SUPPLY annual percent rate (APR) at lending provider.
   *
   * @param vault IVault required by some specific providers with multi-markets, otherwise pass address(0)
   *
   * @dev Requirements:
   * - Must return the rate in ray units (1e27)
   * Example 8.5% APR = 0.085 x 1e27 = 85000000000000000000000000
   * - Must not require Vault context.
   */
  function getDepositRateFor(IVault vault) external view returns (uint256 rate);

  /**
   * @notice Returns the latest BORROW annual percent rate (APR) at lending provider.
   *
   * @param vault IVault required by some specific providers with multi-markets, otherwise pass address(0)
   *
   * @dev Requirements:
   * - Must return the rate in ray units (1e27)
   * Example 8.5% APR = 0.085 x 1e27 = 85000000000000000000000000
   * - Must not require Vault context.
   */
  function getBorrowRateFor(IVault vault) external view returns (uint256 rate);
}

File 21 of 25 : IFujiOracle.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.15;

/**
 * @title IFujiOracle
 *
 * @author Fujidao Labs
 *
 * @notice Defines the interface of the {FujiOracle}.
 */

interface IFujiOracle {
  /**
   * @dev Emit when a change in price feed address is done for an `asset`.
   *
   * @param asset address
   * @param newPriceFeedAddress that returns USD price from Chainlink
   */
  event AssetPriceFeedChanged(address asset, address newPriceFeedAddress);

  /**
   * @notice Returns the exchange rate between two assets, with price oracle given in
   * specified `decimals`.
   *
   * @param currencyAsset to be used, zero-address for USD
   * @param commodityAsset to be used, zero-address for USD
   * @param decimals  of the desired price output
   *
   * @dev Price format is defined as: (amount of currencyAsset per unit of commodityAsset Exchange Rate).
   * Requirements:
   * - Must check that both `currencyAsset` and `commodityAsset` are set in
   *   usdPriceFeeds, otherwise return zero.
   */
  function getPriceOf(
    address currencyAsset,
    address commodityAsset,
    uint8 decimals
  )
    external
    view
    returns (uint256);
}

File 22 of 25 : IAccessControl.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)

pragma solidity ^0.8.0;

/**
 * @dev External interface of AccessControl declared to support ERC165 detection.
 */
interface IAccessControl {
    /**
     * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
     *
     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
     * {RoleAdminChanged} not being emitted signaling this.
     *
     * _Available since v3.1._
     */
    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);

    /**
     * @dev Emitted when `account` is granted `role`.
     *
     * `sender` is the account that originated the contract call, an admin role
     * bearer except when using {AccessControl-_setupRole}.
     */
    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Emitted when `account` is revoked `role`.
     *
     * `sender` is the account that originated the contract call:
     *   - if using `revokeRole`, it is the admin role bearer
     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
     */
    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) external view returns (bool);

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {AccessControl-_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) external view returns (bytes32);

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function grantRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function revokeRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been granted `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `account`.
     */
    function renounceRole(bytes32 role, address account) external;
}

File 23 of 25 : IERC20Metadata.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 *
 * _Available since v4.1._
 */
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 24 of 25 : Context.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}

File 25 of 25 : CoreRoles.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.15;

/**
 * @title CoreRoles
 *
 * @author Fujidao Labs
 *
 * @notice System definition of roles used across FujiV2 contracts.
 */

contract CoreRoles {
  bytes32 public constant HOUSE_KEEPER_ROLE = keccak256("HOUSE_KEEPER_ROLE");

  bytes32 public constant REBALANCER_ROLE = keccak256("REBALANCER_ROLE");
  bytes32 public constant HARVESTER_ROLE = keccak256("HARVESTER_ROLE");
  bytes32 public constant LIQUIDATOR_ROLE = keccak256("LIQUIDATOR_ROLE");

  bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
  bytes32 public constant UNPAUSER_ROLE = keccak256("UNPAUSER_ROLE");
}

Settings
{
  "remappings": [
    "@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
    "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
    "@std/=lib/forge-std/src/",
    "ds-test/=lib/forge-std/lib/ds-test/src/",
    "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
    "forge-std/=lib/forge-std/src/",
    "nxtp/=lib/nxtp/packages/deployments/contracts/contracts/",
    "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/",
    "openzeppelin/=lib/openzeppelin-contracts/contracts/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "bytecodeHash": "ipfs"
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "london",
  "libraries": {}
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"contract IWETH9","name":"weth","type":"address"},{"internalType":"contract IConnext","name":"connext_","type":"address"},{"internalType":"contract IChief","name":"chief","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"BaseRouter__allowCaller_noAllowChange","type":"error"},{"inputs":[],"name":"BaseRouter__allowCaller_zeroAddress","type":"error"},{"inputs":[],"name":"BaseRouter__bundleInternal_flashloanInvalidRequestor","type":"error"},{"inputs":[],"name":"BaseRouter__bundleInternal_insufficientETH","type":"error"},{"inputs":[],"name":"BaseRouter__bundleInternal_insufficientFlashloanBalance","type":"error"},{"inputs":[],"name":"BaseRouter__bundleInternal_noBalanceChange","type":"error"},{"inputs":[],"name":"BaseRouter__bundleInternal_notAllowedFlasher","type":"error"},{"inputs":[],"name":"BaseRouter__bundleInternal_notAllowedSwapper","type":"error"},{"inputs":[],"name":"BaseRouter__bundleInternal_notBeneficiary","type":"error"},{"inputs":[],"name":"BaseRouter__bundleInternal_paramsMismatch","type":"error"},{"inputs":[],"name":"BaseRouter__bundleInternal_swapNotFirstAction","type":"error"},{"inputs":[],"name":"BaseRouter__checkVaultInput_notActiveVault","type":"error"},{"inputs":[],"name":"BaseRouter__fallback_notAllowed","type":"error"},{"inputs":[],"name":"BaseRouter__handlePermit_notPermitAction","type":"error"},{"inputs":[],"name":"BaseRouter__receive_senderNotWETH","type":"error"},{"inputs":[],"name":"BaseRouter__safeTransferETH_transferFailed","type":"error"},{"inputs":[],"name":"ConnextRouter__setRouter_invalidInput","type":"error"},{"inputs":[],"name":"ConnextRouter__xReceive_notAllowedCaller","type":"error"},{"inputs":[],"name":"ConnextRouter__xReceive_notReceivedAssetBalance","type":"error"},{"inputs":[],"name":"ConnextRouter__xReceiver_noValueTransferUseXbundle","type":"error"},{"inputs":[],"name":"ConnnextRouter__xBundleConnext_notSelfCalled","type":"error"},{"inputs":[{"internalType":"address","name":"caller","type":"address"},{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"SystemAccessControl__hasRole_missingRole","type":"error"},{"inputs":[],"name":"SystemAccessControl__onlyHouseKeeper_notHouseKeeper","type":"error"},{"inputs":[],"name":"SystemAccessControl__onlyTimelock_callerIsNotTimelock","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"caller","type":"address"},{"indexed":false,"internalType":"bool","name":"allowed","type":"bool"}],"name":"AllowCaller","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"router","type":"address"},{"indexed":true,"internalType":"uint256","name":"domain","type":"uint256"}],"name":"NewRouterAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"transferId","type":"bytes32"},{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"destDomain","type":"uint256"},{"indexed":false,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"callData","type":"bytes"}],"name":"XCalled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"transferId","type":"bytes32"},{"indexed":true,"internalType":"uint256","name":"originDomain","type":"uint256"},{"indexed":false,"internalType":"bool","name":"success","type":"bool"},{"indexed":false,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"callData","type":"bytes"}],"name":"XReceived","type":"event"},{"stateMutability":"payable","type":"fallback"},{"inputs":[],"name":"HARVESTER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"HOUSE_KEEPER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"LIQUIDATOR_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PAUSER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"REBALANCER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"UNPAUSER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"WETH9","outputs":[{"internalType":"contract IWETH9","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"caller","type":"address"},{"internalType":"bool","name":"allowed","type":"bool"}],"name":"allowCaller","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"transferId","type":"bytes32"}],"name":"bumpTransfer","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"chief","outputs":[{"internalType":"contract IChief","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"connext","outputs":[{"internalType":"contract IConnext","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"handler","outputs":[{"internalType":"contract ConnextHandler","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isAllowedCaller","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"routerByDomain","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"domain","type":"uint256"},{"internalType":"address","name":"router","type":"address"}],"name":"setRouter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"}],"name":"sweepETH","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract ERC20","name":"token","type":"address"},{"internalType":"address","name":"receiver","type":"address"}],"name":"sweepToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"enum IRouter.Action[]","name":"actions","type":"uint8[]"},{"internalType":"bytes[]","name":"args","type":"bytes[]"}],"name":"xBundle","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"enum IRouter.Action[]","name":"actions","type":"uint8[]"},{"internalType":"bytes[]","name":"args","type":"bytes[]"},{"internalType":"uint256","name":"beforeSlipped","type":"uint256"}],"name":"xBundleConnext","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"transferId","type":"bytes32"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"asset","type":"address"},{"internalType":"address","name":"originSender","type":"address"},{"internalType":"uint32","name":"originDomain","type":"uint32"},{"internalType":"bytes","name":"callData","type":"bytes"}],"name":"xReceive","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]

6101006040523480156200001257600080fd5b5060405162005de838038062005de88339810160408190526200003591620001a0565b6001600160a01b0380821660805280841660a052821660c05260405130906200005e9062000179565b6001600160a01b039091168152602001604051809103906000f0801580156200008b573d6000803e3d6000fd5b506001600160a01b031660e052620000a5336001620000ae565b505050620001f4565b6001600160a01b038216620000d657604051632cdf6c4b60e11b815260040160405180910390fd5b6001600160a01b03821660009081526020819052604090205481151560ff90911615150362000118576040516332fe3f1960e21b815260040160405180910390fd5b6001600160a01b03821660008181526020818152604091829020805460ff19168515159081179091558251938452908301527f6bf208b256dacdc89831fd5124ada2aa0f56252c178dd5aff85ee68daf0a7f1c910160405180910390a15050565b61182880620045c083390190565b6001600160a01b03811681146200019d57600080fd5b50565b600080600060608486031215620001b657600080fd5b8351620001c38162000187565b6020850151909350620001d68162000187565b6040850151909250620001e98162000187565b809150509250925092565b60805160a05160c05160e051614306620002ba600039600081816103c801528181610c2b0152610c670152600081816103fc0152818161073901528181612442015281816124c10152818161260f01526126380152600081816101330152818161029f015281816118550152818161187e0152818161194901526119870152600081816104f90152818161051d01528181610686015281816107dd015281816109580152818161167801528181611d060152818161279b01526128a801526143066000f3fe6080604052600436106101235760003560e01c80637270bdb0116100a0578063e63ab1e911610064578063e63ab1e91461041e578063f4200a1414610452578063fb1bb9de14610486578063fd614f41146104ba578063ffd864d3146104e757610173565b80637270bdb014610343578063a3fb20f414610363578063a680125814610376578063c80916d4146103b6578063de4b0548146103ea57610173565b80633fb9a94e116100e75780633fb9a94e14610246578063490b48f8146102595780634aa4a4fc1461028d5780635955003c146102d957806363a560ec1461030f57610173565b8063087de6f71461018c5780631163b2b0146101ac57806316d8887a146101cc5780632424401f14610213578063258836fe1461022657610173565b3661017357336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610171576040516372643aad60e01b815260040160405180910390fd5b005b60405163c6a579c760e01b815260040160405180910390fd5b34801561019857600080fd5b506101716101a7366004613582565b61051b565b3480156101b857600080fd5b506101716101c73660046135b2565b61064b565b3480156101d857600080fd5b506102007f5e17fc5225d4a099df75359ce1f405503ca79498a8dc46a7d583235a0ee45c1681565b6040519081526020015b60405180910390f35b6101716102213660046135d6565b610723565b34801561023257600080fd5b506101716102413660046135ef565b6107a2565b610171610254366004613668565b6108e6565b34801561026557600080fd5b506102007fccc64574297998b6c3edf6078cc5e01268465ff116954e3af02ff3a70a730f4681565b34801561029957600080fd5b506102c17f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b03909116815260200161020a565b3480156102e557600080fd5b506102c16102f43660046135d6565b6003602052600090815260409020546001600160a01b031681565b34801561031b57600080fd5b506102007f3fc733b4d20d27a28452ddf0e9351aced28242fe03389a653cdb783955316b9b81565b34801561034f57600080fd5b5061017161035e3660046136e9565b610956565b610171610371366004613717565b610a13565b34801561038257600080fd5b506103a66103913660046135b2565b60006020819052908152604090205460ff1681565b604051901515815260200161020a565b3480156103c257600080fd5b506102c17f000000000000000000000000000000000000000000000000000000000000000081565b3480156103f657600080fd5b506102c17f000000000000000000000000000000000000000000000000000000000000000081565b34801561042a57600080fd5b506102007f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a81565b34801561045e57600080fd5b506102007fe9e995cadb4a71c3f5032f9fbf95a1e1369b940625a86946b14b2d845bf747ab81565b34801561049257600080fd5b506102007f427da25fe773164f88948d3e215c94b6554e2ed5e5f203a821c9f2f6131cf75a81565b3480156104c657600080fd5b506104da6104d5366004613845565b610a63565b60405161020a919061392c565b3480156104f357600080fd5b506102c17f000000000000000000000000000000000000000000000000000000000000000081565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d33219b46040518163ffffffff1660e01b8152600401602060405180830381865afa158015610579573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061059d919061393f565b6001600160a01b0316336001600160a01b0316146105ce5760405163b909292960e01b815260040160405180910390fd5b6001600160a01b0381166105f55760405163388732eb60e01b815260040160405180910390fd5b60008281526003602052604080822080546001600160a01b0319166001600160a01b038516908117909155905184927f038088a234c0d1d17a5f4c755e87c5283845155cf3344f6c977eedca1275ef1591a35050565b604051632474521560e21b81527fe9e995cadb4a71c3f5032f9fbf95a1e1369b940625a86946b14b2d845bf747ab60048201523360248201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906391d1485490604401602060405180830381865afa1580156106d5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106f9919061395c565b61071657604051639c73fc7f60e01b815260040160405180910390fd5b6107208147610db2565b50565b604051632424401f60e01b8152600481018290527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690632424401f9034906024016000604051808303818588803b15801561078657600080fd5b505af115801561079a573d6000803e3d6000fd5b505050505050565b604051632474521560e21b81527fe9e995cadb4a71c3f5032f9fbf95a1e1369b940625a86946b14b2d845bf747ab60048201523360248201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906391d1485490604401602060405180830381865afa15801561082c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610850919061395c565b61086d57604051639c73fc7f60e01b815260040160405180910390fd5b6040516370a0823160e01b81523060048201526108e290839083906001600160a01b038316906370a0823190602401602060405180830381865afa1580156108b9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108dd9190613979565b610e45565b5050565b33301461090657604051633228cc7160e21b815260040160405180910390fd5b61094f85858080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525061094992508691508790506139b5565b83610ea8565b5050505050565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d33219b46040518163ffffffff1660e01b8152600401602060405180830381865afa1580156109b4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109d8919061393f565b6001600160a01b0316336001600160a01b031614610a095760405163b909292960e01b815260040160405180910390fd5b6108e28282611a16565b610a5d848480806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250610a5692508591508690506139b5565b6000610ea8565b50505050565b606060008083806020019051810190610a7c9190613b60565b90925090506000808915610bc9576040516370a0823160e01b81523060048201526001600160a01b038a16906370a0823190602401602060405180830381865afa158015610ace573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610af29190613979565b915089821015610b155760405163018884f160e51b815260040160405180910390fd5b60405180604001604052808a6001600160a01b031681526020018b84610b3b9190613bd9565b90528051600180546001600160a01b039092166001600160a01b031990921691909117905560200151600255610ba68a85600081518110610b7e57610b7e613bf0565b602002602001015185600081518110610b9957610b99613bf0565b6020026020010151611adf565b84600081518110610bb957610bb9613bf0565b6020026020010181935082905250505b604051631fdcd4a760e11b81523090633fb9a94e90610bf090879087908690600401613cca565b600060405180830381600087803b158015610c0a57600080fd5b505af1925050508015610c1b575060015b610d4d578115610cdb57610c50897f000000000000000000000000000000000000000000000000000000000000000084610e45565b60405163e667df2f60e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063e667df2f90610ca8908e908e908e908e908e908c908c90600401613d00565b600060405180830381600087803b158015610cc257600080fd5b505af1158015610cd6573d6000803e3d6000fd5b505050505b6001600080820160006101000a8154906001600160a01b030219169055600182016000905550508663ffffffff168b7f62277c9bd483b2bd832cf3849e52ec0a2b1e4297f133ccb28e963239a8007f5a60008c8e8b604051610d409493929190613d65565b60405180910390a3610d94565b8663ffffffff168b7f62277c9bd483b2bd832cf3849e52ec0a2b1e4297f133ccb28e963239a8007f5a60018c8e8b604051610d8b9493929190613d65565b60405180910390a35b50506040805160208101909152600081529998505050505050505050565b604080516000808252602082019092526001600160a01b038416908390604051610ddc9190613d9e565b60006040518083038185875af1925050503d8060008114610e19576040519150601f19603f3d011682016040523d82523d6000602084013e610e1e565b606091505b5050905080610e40576040516322462d1f60e01b815260040160405180910390fd5b505050565b6040516001600160a01b038316602482015260448101829052610e4090849063a9059cbb60e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152611c0d565b825182518114610ecb576040516305b9af8f60e51b815260040160405180910390fd5b60408051600a808252610160820190925260009182918291816020015b6040805180820190915260008082526020820152815260200190600190039081610ee85750506001549091506001600160a01b031615610f6157604080518082019091526001546001600160a01b03168152600254602082015281518290600090610f5557610f55613bf0565b60200260200101819052505b6000610f6d3447613bd9565b905060005b85811015611a01576000898281518110610f8e57610f8e613bf0565b602002602001015190506000600b811115610fab57610fab613c06565b81600b811115610fbd57610fbd613c06565b03611119576000806000808c8681518110610fda57610fda613bf0565b6020026020010151806020019051810190610ff59190613dba565b935093509350935061100684611ce7565b6000846001600160a01b03166338d52e0f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611046573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061106a919061393f565b90506110768b84611d8e565b9a50611082818a611de0565b985061108f818386611eb9565b61109a818686611eeb565b604051636e553f6560e01b8152600481018590526001600160a01b038481166024830152861690636e553f65906044015b6020604051808303816000875af11580156110ea573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061110e9190613979565b5050505050506119f8565b600181600b81111561112d5761112d613c06565b03611277576000806000808c868151811061114a5761114a613bf0565b60200260200101518060200190518101906111659190613dba565b935093509350935061117684611ce7565b6111808a82611d8e565b99506111ed846001600160a01b03166338d52e0f6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156111c3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111e7919061393f565b89611de0565b604051632d182be560e21b8152600481018590526001600160a01b03848116602483015283811660448301529199509085169063b460af94906064015b6020604051808303816000875af1158015611249573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061126d9190613979565b50505050506119f8565b600281600b81111561128b5761128b613c06565b03611362576000806000808c86815181106112a8576112a8613bf0565b60200260200101518060200190518101906112c39190613dba565b93509350935093506112d484611ce7565b6112de8a82611d8e565b9950611321846001600160a01b031663a919802d6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156111c3573d6000803e3d6000fd5b604051633545906160e21b8152600481018590526001600160a01b03848116602483015283811660448301529199509085169063d51641849060640161122a565b600381600b81111561137657611376613c06565b03611488576000806000808c868151811061139357611393613bf0565b60200260200101518060200190518101906113ae9190613dba565b93509350935093506113bf84611ce7565b6000846001600160a01b031663a919802d6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156113ff573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611423919061393f565b905061142f8b84611d8e565b9a5061143b818a611de0565b9850611448818386611eb9565b611453818686611eeb565b60405163d084b9af60e01b8152600481018590526001600160a01b03848116602483015286169063d084b9af906044016110cb565b600681600b81111561149c5761149c613c06565b036114ee57846114b4576114b18a8a8a611ef6565b94505b60006114da828b85815181106114cc576114cc613bf0565b602002602001015188612221565b90506114e68782611d8e565b9650506119f8565b600781600b81111561150257611502613c06565b0361151757846114b4576114b18a8a8a611ef6565b600881600b81111561152b5761152b613c06565b0361155a5761155389838151811061154557611545613bf0565b6020026020010151876123f8565b95506119f8565b600981600b81111561156e5761156e613c06565b036115965761155389838151811061158857611588613bf0565b6020026020010151876125a2565b600581600b8111156115aa576115aa613c06565b036115fe57816000036115d05760405163b42fccab60e01b815260040160405180910390fd5b6115f48983815181106115e5576115e5613bf0565b60200260200101518786612765565b90965093506119f8565b600481600b81111561161257611612613c06565b036117e65760008060008060008d878151811061163157611631613bf0565b602002602001015180602001905181019061164c9190613e0f565b6040516306a33c7960e51b81526001600160a01b038681166004830152959a50939850919650945092507f00000000000000000000000000000000000000000000000000000000000000009091169063d4678f2090602401602060405180830381865afa1580156116c1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116e5919061395c565b61170257604051631ddd578f60e11b815260040160405180910390fd5b6001600160a01b038216301461172b5760405163ef34858960e01b815260040160405180910390fd5b611735848a611de0565b985060008061175283600480865161174d9190613bd9565b6129df565b8060200190518101906117659190613b60565b915091506117738282612aec565b604051631f8aa39560e21b8152909d506001600160a01b03881690637e2a8e54906117a8908990899089908990600401613e90565b600060405180830381600087803b1580156117c257600080fd5b505af11580156117d6573d6000803e3d6000fd5b50505050505050505050506119f8565b600a81600b8111156117fa576117fa613c06565b036118eb57600089838151811061181357611813613bf0565b602002602001015180602001905181019061182e9190613979565b9050348114611850576040516379eecdfb60e11b815260040160405180910390fd5b61187a7f000000000000000000000000000000000000000000000000000000000000000086611de0565b94507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d0e30db0346040518263ffffffff1660e01b81526004016000604051808303818588803b1580156118d757600080fd5b505af115801561110e573d6000803e3d6000fd5b600b81600b8111156118ff576118ff613c06565b036119f8576000808a848151811061191957611919613bf0565b60200260200101518060200190518101906119349190613ec3565b915091506119428882611d8e565b975061196e7f000000000000000000000000000000000000000000000000000000000000000087611de0565b604051632e1a7d4d60e01b8152600481018490529096507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690632e1a7d4d90602401600060405180830381600087803b1580156119d357600080fd5b505af11580156119e7573d6000803e3d6000fd5b505050506119f58183610db2565b50505b50600101610f72565b50611a0c82826130ef565b5050505050505050565b6001600160a01b038216611a3d57604051632cdf6c4b60e11b815260040160405180910390fd5b6001600160a01b03821660009081526020819052604090205481151560ff909116151503611a7e576040516332fe3f1960e21b815260040160405180910390fd5b6001600160a01b03821660008181526020818152604091829020805460ff19168515159081179091558251938452908301527f6bf208b256dacdc89831fd5124ada2aa0f56252c178dd5aff85ee68daf0a7f1c910160405180910390a15050565b8060008084600b811115611af557611af5613c06565b1480611b125750600384600b811115611b1057611b10613c06565b145b15611b8d5760008060008086806020019051810190611b319190613dba565b9350935093509350888314611b8457604080516001600160a01b0380871660208301529181018b905281841660608201529082166080820152929450849260a00160405160208183030381529060405295505b50505050611c05565b600b84600b811115611ba157611ba1613c06565b03611c055760008084806020019051810190611bbd9190613ec3565b91509150868214611c02578192508681604051602001611bf09291909182526001600160a01b0316602082015260400190565b60405160208183030381529060405293505b50505b935093915050565b6000611c62826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b031661322c9092919063ffffffff16565b9050805160001480611c83575080806020019051810190611c83919061395c565b610e405760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084015b60405180910390fd5b604051630c5aed5760e01b81526001600160a01b0382811660048301527f00000000000000000000000000000000000000000000000000000000000000001690630c5aed5790602401602060405180830381865afa158015611d4d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d71919061395c565b6107205760405163106f25d560e31b815260040160405180910390fd5b60006001600160a01b038316611da5575080611dda565b816001600160a01b0316836001600160a01b031614611dd757604051630727952560e41b815260040160405180910390fd5b50805b92915050565b6060600080611def8585613243565b9150915081611eb05760008115611e1057611e0b826001613ee8565b611e12565b815b6040805180820182526001600160a01b03891680825291516370a0823160e01b8152306004820152929350916020830191906370a0823190602401602060405180830381865afa158015611e6a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e8e9190613979565b815250858281518110611ea357611ea3613bf0565b6020026020010181905250505b50919392505050565b6001600160a01b0382163014801590611eda57506001600160a01b03821633145b15610e4057610e40838330846132a7565b610e408383836132df565b825160009081816001600160401b03811115611f1457611f14613782565b604051908082528060200260200182016040528015611f4757816020015b6060815260200190600190039081611f325790505b50905060005b828110156121ec57858181518110611f6757611f67613bf0565b6020026020010151828281518110611f8157611f81613bf0565b6020026020010181905250806000148015611f9b57508415155b801561200557506000878281518110611fb657611fb6613bf0565b6020026020010151600b811115611fcf57611fcf613c06565b148061200557506003878281518110611fea57611fea613bf0565b6020026020010151600b81111561200357612003613c06565b145b156120ab5760008060008085858151811061202257612022613bf0565b602002602001015180602001905181019061203d9190613dba565b93509350935093508289146120a657604080516001600160a01b0380871660208301529181018b90528184166060820152908216608082015260a00160405160208183030381529060405286868151811061209a5761209a613bf0565b60200260200101819052505b505050505b60068782815181106120bf576120bf613bf0565b6020026020010151600b8111156120d8576120d8613c06565b148061210e575060078782815181106120f3576120f3613bf0565b6020026020010151600b81111561210c5761210c613c06565b145b156121da5760008060008085858151811061212b5761212b613bf0565b60200260200101518060200190518101906121469190613f00565b5050604080516001600160a01b038089166020830152808816828401528616606082015260808101859052600060a0820181905260c0820181905260e08201819052610100808301919091528251808303909101815261012090910190915295995093975091955093506121b8915050565b8686815181106121ca576121ca613bf0565b6020026020010181905250505050505b806121e481613f8c565b915050611f4d565b508581604051602001612200929190613fa5565b60405160208183030381529060405280519060200120925050509392505050565b6040805161010081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e08101829052838060200190518101906122759190613f00565b60e089015260c088015260ff1660a0870152608086015260608501526001600160a01b0390811660408501529081166020840152168082526122b690611ce7565b600685600b8111156122ca576122ca613c06565b036123605780600001516001600160a01b0316630fad31318260200151836040015184606001518560800151888760a001518860c001518960e001516040518963ffffffff1660e01b8152600401612329989796959493929190613fd3565b600060405180830381600087803b15801561234357600080fd5b505af1158015612357573d6000803e3d6000fd5b505050506123ec565b600785600b81111561237457612374613c06565b036123d35780600001516001600160a01b03166396ebbc458260200151836040015184606001518560800151888760a001518860c001518960e001516040518963ffffffff1660e01b8152600401612329989796959493929190613fd3565b604051632f67478960e21b815260040160405180910390fd5b60200151949350505050565b600080600080600080600088806020019051810190612417919061401a565b955095509550955095509550600061242f8984611d8e565b905061243c858386611eb9565b612467857f000000000000000000000000000000000000000000000000000000000000000086611eeb565b6040516345560b5d60e11b815263ffffffff881660048201526001600160a01b038481166024830152868116604483015282811660648301526084820186905260a4820188905260e060c4830152600060e48301819052917f000000000000000000000000000000000000000000000000000000000000000090911690638aac16ba90610104016020604051808303816000875af115801561250d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125319190613979565b604080518a81526001600160a01b038981166020830152918101889052608060608201819052600090820152919250851690339083907fb7c6eb876c99dadc81a4df296037aa08e5c6be07a3765524cf8812aec701f7e49060a00160405180910390a4509998505050505050505050565b6000806000806000806000888060200190518101906125c19190614083565b955095509550955095509550600080828060200190518101906125e491906140f5565b50915091506125fc8a6125f78484612aec565b611d8e565b9850612609868587611eb9565b612634867f000000000000000000000000000000000000000000000000000000000000000087611eeb565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316638aac16ba8a600360008d815260200190815260200160002060009054906101000a90046001600160a01b03168a8e8b8e8b6040518863ffffffff1660e01b81526004016126b49796959493929190614161565b6020604051808303816000875af11580156126d3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126f79190613979565b60008a815260036020526040908190205490519192506001600160a01b031690339083907fb7c6eb876c99dadc81a4df296037aa08e5c6be07a3765524cf8812aec701f7e49061274e908e908d908d908c906141bc565b60405180910390a450505050505050505092915050565b600060606000806000806000806000808c80602001905181019061278991906141e9565b975097509750975097509750975097507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663877945f0896040518263ffffffff1660e01b81526004016127f491906001600160a01b0391909116815260200190565b602060405180830381865afa158015612811573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612835919061395c565b6128525760405163418f824160e01b815260040160405180910390fd5b61285c878c611de0565b9a50612868868c611de0565b9a50612875878987611eeb565b6001600160a01b038316301480159061291557506040516306a33c7960e51b81526001600160a01b0384811660048301527f0000000000000000000000000000000000000000000000000000000000000000169063d4678f2090602401602060405180830381865afa1580156128ef573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612913919061395c565b155b15612927576129248c84611d8e565b9b505b6001600160a01b0382163014612944576129418c83611d8e565b9b505b60405163e2c4f23b60e01b81526001600160a01b03888116600483015287811660248301526044820187905260648201869052848116608483015283811660a483015260c4820183905289169063e2c4f23b9060e401600060405180830381600087803b1580156129b457600080fd5b505af11580156129c8573d6000803e3d6000fd5b509d9f9c9e509b9c50505050505050505050505050565b6060816129ed81601f613ee8565b1015612a2c5760405162461bcd60e51b815260206004820152600e60248201526d736c6963655f6f766572666c6f7760901b6044820152606401611cde565b612a368284613ee8565b84511015612a7a5760405162461bcd60e51b8152602060048201526011602482015270736c6963655f6f75744f66426f756e647360781b6044820152606401611cde565b606082158015612a995760405191506000825260208201604052612ae3565b6040519150601f8416801560200281840101858101878315602002848b0101015b81831015612ad2578051835260209283019201612aba565b5050858452601f01601f1916604052505b50949350505050565b60008083600081518110612b0257612b02613bf0565b6020026020010151600b811115612b1b57612b1b613c06565b1480612b525750600383600081518110612b3757612b37613bf0565b6020026020010151600b811115612b5057612b50613c06565b145b15612b9357600082600081518110612b6c57612b6c613bf0565b6020026020010151806020019051810190612b879190613dba565b509350611dda92505050565b600183600081518110612ba857612ba8613bf0565b6020026020010151600b811115612bc157612bc1613c06565b1480612bf85750600283600081518110612bdd57612bdd613bf0565b6020026020010151600b811115612bf657612bf6613c06565b145b15612c3957600082600081518110612c1257612c12613bf0565b6020026020010151806020019051810190612c2d9190613dba565b9450611dda9350505050565b600b83600081518110612c4e57612c4e613bf0565b6020026020010151600b811115612c6757612c67613c06565b03612ca657600082600081518110612c8157612c81613bf0565b6020026020010151806020019051810190612c9c9190613ec3565b9250611dda915050565b600783600081518110612cbb57612cbb613bf0565b6020026020010151600b811115612cd457612cd4613c06565b1480612d0b5750600683600081518110612cf057612cf0613bf0565b6020026020010151600b811115612d0957612d09613c06565b145b15612d5157600082600081518110612d2557612d25613bf0565b6020026020010151806020019051810190612d409190613f00565b50949750611dda9650505050505050565b600483600081518110612d6657612d66613bf0565b6020026020010151600b811115612d7f57612d7f613c06565b03612dfb57600082600081518110612d9957612d99613bf0565b6020026020010151806020019051810190612db49190613e0f565b945050505050600080612dd083600480865161174d9190613bd9565b806020019051810190612de39190613b60565b91509150612df18282612aec565b9350505050611dda565b600883600081518110612e1057612e10613bf0565b6020026020010151600b811115612e2957612e29613c06565b03612e6c57600082600081518110612e4357612e43613bf0565b6020026020010151806020019051810190612e5e919061401a565b509550611dda945050505050565b600983600081518110612e8157612e81613bf0565b6020026020010151600b811115612e9a57612e9a613c06565b03612efb57600082600081518110612eb457612eb4613bf0565b6020026020010151806020019051810190612ecf919061427c565b94505050505060008082806020019051810190612eec91906140f5565b5091509150612df18282612aec565b600a83600081518110612f1057612f10613bf0565b6020026020010151600b811115612f2957612f29613c06565b036130a35782516000612f3d600183613bd9565b6001600160401b03811115612f5457612f54613782565b604051908082528060200260200182016040528015612f7d578160200160208202803683370190505b5090506000612f8d600184613bd9565b6001600160401b03811115612fa457612fa4613782565b604051908082528060200260200182016040528015612fd757816020015b6060815260200190600190039081612fc25790505b50905060015b8381101561309857868181518110612ff757612ff7613bf0565b60200260200101518360018361300d9190613bd9565b8151811061301d5761301d613bf0565b6020026020010190600b81111561303657613036613c06565b9081600b81111561304957613049613c06565b8152505085818151811061305f5761305f613bf0565b6020026020010151826001836130759190613bd9565b8151811061308557613085613bf0565b6020908102919091010152600101612fdd565b50612df18282612aec565b6005836000815181106130b8576130b8613bf0565b6020026020010151600b8111156130d1576130d1613c06565b03611dda5760405163b42fccab60e01b815260040160405180910390fd5b815160005b8181101561320b5760006001600160a01b031684828151811061311957613119613bf0565b6020026020010151600001516001600160a01b03161461320357600084828151811061314757613147613bf0565b6020026020010151602001519050600085838151811061316957613169613bf0565b6020908102919091010151516040516370a0823160e01b81523060048201526001600160a01b03909116906370a0823190602401602060405180830381865afa1580156131ba573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131de9190613979565b905081811461320057604051632c1b2f5f60e21b815260040160405180910390fd5b50505b6001016130f4565b50478214610e4057604051632c1b2f5f60e21b815260040160405180910390fd5b606061323b84846000856133f4565b949350505050565b80516000908190815b8181101561329e5784818151811061326657613266613bf0565b6020026020010151600001516001600160a01b0316866001600160a01b031603613296576001935080925061329e565b60010161324c565b50509250929050565b6040516001600160a01b0380851660248301528316604482015260648101829052610a5d9085906323b872dd60e01b90608401610e71565b8015806133595750604051636eb1769f60e11b81523060048201526001600160a01b03838116602483015284169063dd62ed3e90604401602060405180830381865afa158015613333573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133579190613979565b155b6133c45760405162461bcd60e51b815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527520746f206e6f6e2d7a65726f20616c6c6f77616e636560501b6064820152608401611cde565b6040516001600160a01b038316602482015260448101829052610e4090849063095ea7b360e01b90606401610e71565b6060824710156134555760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401611cde565b600080866001600160a01b031685876040516134719190613d9e565b60006040518083038185875af1925050503d80600081146134ae576040519150601f19603f3d011682016040523d82523d6000602084013e6134b3565b606091505b50915091506134c4878383876134cf565b979650505050505050565b6060831561353e578251600003613537576001600160a01b0385163b6135375760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401611cde565b508161323b565b61323b83838151156135535781518083602001fd5b8060405162461bcd60e51b8152600401611cde919061392c565b6001600160a01b038116811461072057600080fd5b6000806040838503121561359557600080fd5b8235915060208301356135a78161356d565b809150509250929050565b6000602082840312156135c457600080fd5b81356135cf8161356d565b9392505050565b6000602082840312156135e857600080fd5b5035919050565b6000806040838503121561360257600080fd5b823561360d8161356d565b915060208301356135a78161356d565b60008083601f84011261362f57600080fd5b5081356001600160401b0381111561364657600080fd5b6020830191508360208260051b850101111561366157600080fd5b9250929050565b60008060008060006060868803121561368057600080fd5b85356001600160401b038082111561369757600080fd5b6136a389838a0161361d565b909750955060208801359150808211156136bc57600080fd5b506136c98882890161361d565b96999598509660400135949350505050565b801515811461072057600080fd5b600080604083850312156136fc57600080fd5b82356137078161356d565b915060208301356135a7816136db565b6000806000806040858703121561372d57600080fd5b84356001600160401b038082111561374457600080fd5b6137508883890161361d565b9096509450602087013591508082111561376957600080fd5b506137768782880161361d565b95989497509550505050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b03811182821017156137c0576137c0613782565b604052919050565b60006001600160401b038211156137e1576137e1613782565b50601f01601f191660200190565b600082601f83011261380057600080fd5b813561381361380e826137c8565b613798565b81815284602083860101111561382857600080fd5b816020850160208301376000918101602001919091529392505050565b60008060008060008060c0878903121561385e57600080fd5b863595506020870135945060408701356138778161356d565b935060608701356138878161356d565b9250608087013563ffffffff811681146138a057600080fd5b915060a08701356001600160401b038111156138bb57600080fd5b6138c789828a016137ef565b9150509295509295509295565b60005b838110156138ef5781810151838201526020016138d7565b83811115610a5d5750506000910152565b600081518084526139188160208601602086016138d4565b601f01601f19169290920160200192915050565b6020815260006135cf6020830184613900565b60006020828403121561395157600080fd5b81516135cf8161356d565b60006020828403121561396e57600080fd5b81516135cf816136db565b60006020828403121561398b57600080fd5b5051919050565b60006001600160401b038211156139ab576139ab613782565b5060051b60200190565b60006139c361380e84613992565b80848252602080830192508560051b8501368111156139e157600080fd5b855b81811015613a1c5780356001600160401b03811115613a025760008081fd5b613a0e36828a016137ef565b8652509382019382016139e3565b50919695505050505050565b600082601f830112613a3957600080fd5b81516020613a4961380e83613992565b82815260059290921b84018101918181019086841115613a6857600080fd5b8286015b84811015613a91578051600c8110613a845760008081fd5b8352918301918301613a6c565b509695505050505050565b600082601f830112613aad57600080fd5b8151613abb61380e826137c8565b818152846020838601011115613ad057600080fd5b61323b8260208301602087016138d4565b600082601f830112613af257600080fd5b81516020613b0261380e83613992565b82815260059290921b84018101918181019086841115613b2157600080fd5b8286015b84811015613a915780516001600160401b03811115613b445760008081fd5b613b528986838b0101613a9c565b845250918301918301613b25565b60008060408385031215613b7357600080fd5b82516001600160401b0380821115613b8a57600080fd5b613b9686838701613a28565b93506020850151915080821115613bac57600080fd5b50613bb985828601613ae1565b9150509250929050565b634e487b7160e01b600052601160045260246000fd5b600082821015613beb57613beb613bc3565b500390565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052602160045260246000fd5b60008151808452602080850194508084016000805b84811015613c69578251600c8110613c5757634e487b7160e01b83526021600452602483fd5b88529683019691830191600101613c31565b50959695505050505050565b600081518084526020808501808196508360051b8101915082860160005b85811015613cbd578284038952613cab848351613900565b98850198935090840190600101613c93565b5091979650505050505050565b606081526000613cdd6060830186613c1c565b8281036020840152613cef8186613c75565b915050826040830152949350505050565b878152602081018790526001600160a01b0386811660408301528516606082015263ffffffff8416608082015260e060a08201819052600090613d4590830185613c1c565b82810360c0840152613d578185613c75565b9a9950505050505050505050565b841515815260018060a01b0384166020820152826040820152608060608201526000613d946080830184613900565b9695505050505050565b60008251613db08184602087016138d4565b9190910192915050565b60008060008060808587031215613dd057600080fd5b8451613ddb8161356d565b602086015160408701519195509350613df38161356d565b6060860151909250613e048161356d565b939692955090935050565b600080600080600060a08688031215613e2757600080fd5b8551613e328161356d565b6020870151909550613e438161356d565b604087015160608801519195509350613e5b8161356d565b60808701519092506001600160401b03811115613e7757600080fd5b613e8388828901613a9c565b9150509295509295909350565b6001600160a01b0385811682526020820185905283166040820152608060608201819052600090613d9490830184613900565b60008060408385031215613ed657600080fd5b8251915060208301516135a78161356d565b60008219821115613efb57613efb613bc3565b500190565b600080600080600080600080610100898b031215613f1d57600080fd5b8851613f288161356d565b60208a0151909850613f398161356d565b60408a0151909750613f4a8161356d565b80965050606089015194506080890151935060a089015160ff81168114613f7057600080fd5b60c08a015160e0909a0151989b979a5095989497939692505050565b600060018201613f9e57613f9e613bc3565b5060010190565b604081526000613fb86040830185613c1c565b8281036020840152613fca8185613c75565b95945050505050565b6001600160a01b03988916815296909716602087015260408601949094526060850192909252608084015260ff1660a083015260c082015260e08101919091526101000190565b60008060008060008060c0878903121561403357600080fd5b8651955060208701519450604087015161404c8161356d565b6060880151608089015191955093506140648161356d565b60a08801519092506140758161356d565b809150509295509295509295565b60008060008060008060c0878903121561409c57600080fd5b865195506020870151945060408701516140b58161356d565b6060880151608089015191955093506140cd8161356d565b60a08801519092506001600160401b038111156140e957600080fd5b6138c789828a01613a9c565b60008060006060848603121561410a57600080fd5b83516001600160401b038082111561412157600080fd5b61412d87838801613a28565b9450602086015191508082111561414357600080fd5b5061415086828701613ae1565b925050604084015190509250925092565b63ffffffff881681526001600160a01b0387811660208301528681166040830152851660608201526080810184905260a0810183905260e060c082018190526000906141af90830184613900565b9998505050505050505050565b84815260018060a01b0384166020820152826040820152608060608201526000613d946080830184613900565b600080600080600080600080610100898b03121561420657600080fd5b88516142118161356d565b60208a01519098506142228161356d565b60408a01519097506142338161356d565b80965050606089015194506080890151935060a08901516142538161356d565b60c08a01519093506142648161356d565b8092505060e089015190509295985092959890939650565b600080600080600060a0868803121561429457600080fd5b855194506020860151935060408601516142ad8161356d565b6060870151608088015191945092506001600160401b03811115613e7757600080fdfea2646970667358221220895d0472be94d1dce0b168504cb164620e17c452cff2b751dc482ac28487208264736f6c634300080f003360a060405234801561001057600080fd5b5060405161182838038061182883398101604081905261002f91610040565b6001600160a01b0316608052610070565b60006020828403121561005257600080fd5b81516001600160a01b038116811461006957600080fd5b9392505050565b6080516117816100a760003960008181610107015281816101560152818161046a015281816104ff0152610bb901526117816000f3fe608060405234801561001057600080fd5b50600436106100625760003560e01c806304395356146100675780631e8ce9b71461007c5780633347b553146100ac578063c0f3bc28146100cc578063e667df2f146100ef578063f36e558914610102575b600080fd5b61007a610075366004611147565b610141565b005b61008f61008a3660046111c5565b610712565b6040516001600160801b0390911681526020015b60405180910390f35b6100bf6100ba3660046111de565b610743565b6040516100a39190611317565b6100df6100da3660046111de565b610986565b60405190151581526020016100a3565b61007a6100fd3660046113f1565b610bae565b6101297f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016100a3565b6040516314d0024b60e31b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063a680125890602401602060405180830381865afa1580156101a5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101c991906114a5565b6101e657604051631bd4dfdf60e01b815260040160405180910390fd5b6000848152602081815260408083206001600160801b038716845282528083208151610120810183528154815260018201548185015260028201546001600160a01b039081168285015260038301549081166060830152600160a01b900463ffffffff1660808201526004820180548451818702810187019095528085529194929360a08601939092908301828280156102cf57602002820191906000526020600020906000905b82829054906101000a900460ff16600b8111156102ad576102ad61120a565b81526020600192830181810494850194909303909202910180841161028e5790505b5050505050815260200160058201805480602002602001604051908101604052809291908181526020016000905b828210156103a957838290600052602060002001805461031c906114ce565b80601f0160208091040260200160405190810160405280929190818152602001828054610348906114ce565b80156103955780601f1061036a57610100808354040283529160200191610395565b820191906000526020600020905b81548152906001019060200180831161037857829003601f168201915b5050505050815260200190600101906102fd565b50505090825250600691909101546001600160801b0381166020830152600160801b900460ff161515604090910152805190915015806103f15750608081015163ffffffff16155b1561040f5760405163f359c15960e01b815260040160405180910390fd5b8061010001511561044957604051630a0ab2df60e01b8152600481018690526001600160801b038516602482015260440160405180910390fd5b6040818101516020830151915163095ea7b360e01b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081166004830152602482019390935291169063095ea7b3906044016020604051808303816000875af11580156104c3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104e791906114a5565b506040516328fec83d60e21b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063a3fb20f4906105369086908690600401611537565b600060405180830381600087803b15801561055057600080fd5b505af1925050508015610561575060015b6105b65760001515857f0b927ce548896758f1bc8bb29b6650e47491626f1b708ab99a46412f5322445d8360a00151868560c00151878a6040516105a9959493929190611565565b60405180910390a361070b565b600161010082018190526000868152602081815260408083206001600160801b0389168452825291829020845181558185015193810193909355908301516002830180546001600160a01b039283166001600160a01b03199091161790556060840151600384018054608087015163ffffffff16600160a01b026001600160c01b0319909116929093169190911791909117905560a08301518051849392610665926004850192910190610dee565b5060c08201518051610681916005840191602090910190610ea2565b5060e082015160069091018054610100909301511515600160801b026001600160881b03199093166001600160801b039092169190911791909117905560a081015160c082015160405160019288927f0b927ce548896758f1bc8bb29b6650e47491626f1b708ab99a46412f5322445d926107029291899189908c90611565565b60405180910390a35b5050505050565b6000805b60ff81101561073d576107298382610986565b6107355780915061073d565b600101610716565b50919050565b604080516101208101825260008082526020820181905291810182905260608082018390526080820183905260a0820181905260c082015260e081018290526101008101919091526000838152602081815260408083206001600160801b03861684528252918290208251610120810184528154815260018201548184015260028201546001600160a01b039081168286015260038301549081166060830152600160a01b900463ffffffff1660808201526004820180548551818602810186019096528086529194929360a0860193929083018282801561087457602002820191906000526020600020906000905b82829054906101000a900460ff16600b8111156108525761085261120a565b8152602060019283018181049485019490930390920291018084116108335790505b5050505050815260200160058201805480602002602001604051908101604052809291908181526020016000905b8282101561094e5783829060005260206000200180546108c1906114ce565b80601f01602080910402602001604051908101604052809291908181526020018280546108ed906114ce565b801561093a5780601f1061090f5761010080835404028352916020019161093a565b820191906000526020600020905b81548152906001019060200180831161091d57829003601f168201915b5050505050815260200190600101906108a2565b50505090825250600691909101546001600160801b0381166020830152600160801b900460ff16151560409091015290505b92915050565b6000828152602081815260408083206001600160801b038516845282528083208151610120810183528154815260018201548185015260028201546001600160a01b039081168285015260038301549081166060830152600160a01b900463ffffffff1660808201526004820180548451818702810187019095528085528695929460a086019390929190830182828015610a7057602002820191906000526020600020906000905b82829054906101000a900460ff16600b811115610a4e57610a4e61120a565b815260206001928301818104948501949093039092029101808411610a2f5790505b5050505050815260200160058201805480602002602001604051908101604052809291908181526020016000905b82821015610b4a578382906000526020600020018054610abd906114ce565b80601f0160208091040260200160405190810160405280929190818152602001828054610ae9906114ce565b8015610b365780601f10610b0b57610100808354040283529160200191610b36565b820191906000526020600020905b815481529060010190602001808311610b1957829003601f168201915b505050505081526020019060010190610a9e565b50505090825250600691909101546001600160801b0381166020830152600160801b900460ff161515604090910152805190915015801590610b955750608081015163ffffffff1615155b15610ba4576001915050610980565b6000915050610980565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610bf757604051631bd4dfdf60e01b815260040160405180910390fd5b6000610c0288610712565b9050604051806101200160405280898152602001888152602001876001600160a01b03168152602001866001600160a01b031681526020018563ffffffff168152602001848152602001838152602001826001600160801b03168152602001600015158152506000808a81526020019081526020016000206000836001600160801b03168152602001908152602001600020600082015181600001556020820151816001015560408201518160020160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060608201518160030160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060808201518160030160146101000a81548163ffffffff021916908363ffffffff16021790555060a0820151816004019080519060200190610d45929190610dee565b5060c08201518051610d61916005840191602090910190610ea2565b5060e082015160069091018054610100909301511515600160801b026001600160881b03199093166001600160801b039092169190911791909117905560405188907fd44739c3e4748444a6d47fdca8f41581159e21e0a7b0ccc81e492a4ae67b5f2590610ddc908a908a908a908a908a908a908a906115ce565b60405180910390a25050505050505050565b82805482825590600052602060002090601f01602090048101928215610e925791602002820160005b83821115610e6357835183826101000a81548160ff0219169083600b811115610e4257610e4261120a565b02179055509260200192600101602081600001049283019260010302610e17565b8015610e905782816101000a81549060ff0219169055600101602081600001049283019260010302610e63565b505b50610e9e929150610ef4565b5090565b828054828255906000526020600020908101928215610ee8579160200282015b82811115610ee85782518290610ed8908261168b565b5091602001919060010190610ec2565b50610e9e929150610f09565b5b80821115610e9e5760008155600101610ef5565b80821115610e9e576000610f1d8282610f26565b50600101610f09565b508054610f32906114ce565b6000825580601f10610f42575050565b601f016020900490600052602060002090810190610f609190610ef4565b50565b80356001600160801b0381168114610f7a57600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff81118282101715610fbe57610fbe610f7f565b604052919050565b600067ffffffffffffffff821115610fe057610fe0610f7f565b5060051b60200190565b600082601f830112610ffb57600080fd5b8135602061101061100b83610fc6565b610f95565b82815260059290921b8401810191818101908684111561102f57600080fd5b8286015b84811015611058578035600c811061104b5760008081fd5b8352918301918301611033565b509695505050505050565b6000601f838184011261107557600080fd5b8235602061108561100b83610fc6565b82815260059290921b850181019181810190878411156110a457600080fd5b8287015b8481101561113b57803567ffffffffffffffff808211156110c95760008081fd5b818a0191508a603f8301126110de5760008081fd5b858201356040828211156110f4576110f4610f7f565b611105828b01601f19168901610f95565b92508183528c8183860101111561111c5760008081fd5b81818501898501375060009082018701528452509183019183016110a8565b50979650505050505050565b6000806000806080858703121561115d57600080fd5b8435935061116d60208601610f63565b9250604085013567ffffffffffffffff8082111561118a57600080fd5b61119688838901610fea565b935060608701359150808211156111ac57600080fd5b506111b987828801611063565b91505092959194509250565b6000602082840312156111d757600080fd5b5035919050565b600080604083850312156111f157600080fd5b8235915061120160208401610f63565b90509250929050565b634e487b7160e01b600052602160045260246000fd5b6000600c821061124057634e487b7160e01b600052602160045260246000fd5b50815260200190565b600081518084526020808501945080840160005b8381101561127e57611270878351611220565b96509082019060010161125d565b509495945050505050565b600081518084526020808501808196508360051b810191508286016000805b86811015611309578385038a5282518051808752835b818110156112d9578281018901518882018a015288016112be565b818111156112e9578489838a0101525b509a87019a601f01601f19169590950186019450918501916001016112a8565b509298975050505050505050565b6020815281516020820152602082015160408201526000604083015161134860608401826001600160a01b03169052565b5060608301516001600160a01b038116608084015250608083015163ffffffff811660a08401525060a08301516101208060c085015261138c610140850183611249565b915060c0850151601f198584030160e08601526113a98382611289565b92505060e08501516101006113c8818701836001600160801b03169052565b90950151151593019290925250919050565b80356001600160a01b0381168114610f7a57600080fd5b600080600080600080600060e0888a03121561140c57600080fd5b8735965060208801359550611423604089016113da565b9450611431606089016113da565b9350608088013563ffffffff8116811461144a57600080fd5b925060a088013567ffffffffffffffff8082111561146757600080fd5b6114738b838c01610fea565b935060c08a013591508082111561148957600080fd5b506114968a828b01611063565b91505092959891949750929550565b6000602082840312156114b757600080fd5b815180151581146114c757600080fd5b9392505050565b600181811c908216806114e257607f821691505b60208210810361073d57634e487b7160e01b600052602260045260246000fd5b600081518084526020808501945080840160005b8381101561127e57611529878351611220565b965090820190600101611516565b60408152600061154a6040830185611502565b828103602084015261155c8185611289565b95945050505050565b60a08152600061157860a0830188611502565b828103602084015261158a8188611502565b9050828103604084015261159e8187611289565b905082810360608401526115b28186611289565b9150506001600160801b03831660808301529695505050505050565b8781526001600160a01b0387811660208301528616604082015263ffffffff8516606082015260e06080820181905260009061160c90830186611502565b82810360a084015261161e8186611289565b9150506001600160801b03831660c083015298975050505050505050565b601f82111561168657600081815260208120601f850160051c810160208610156116635750805b601f850160051c820191505b818110156116825782815560010161166f565b5050505b505050565b815167ffffffffffffffff8111156116a5576116a5610f7f565b6116b9816116b384546114ce565b8461163c565b602080601f8311600181146116ee57600084156116d65750858301515b600019600386901b1c1916600185901b178555611682565b600085815260208120601f198616915b8281101561171d578886015182559484019460019091019084016116fe565b508582101561173b5787850151600019600388901b60f8161c191681555b5050505050600190811b0190555056fea26469706673582212205d5e946ee46c8ef7c662a2c5b67e0091b28cb9005fb54c720e57fa60e218121164736f6c634300080f00330000000000000000000000007ceb23fd6bc0add59e62ac25578270cff1b9f61900000000000000000000000011984dc4465481512eb5b777e44061c158cf225900000000000000000000000069097538190ac1bceaa0c8ebfa7c512b1eb8d24a

Deployed Bytecode

0x6080604052600436106101235760003560e01c80637270bdb0116100a0578063e63ab1e911610064578063e63ab1e91461041e578063f4200a1414610452578063fb1bb9de14610486578063fd614f41146104ba578063ffd864d3146104e757610173565b80637270bdb014610343578063a3fb20f414610363578063a680125814610376578063c80916d4146103b6578063de4b0548146103ea57610173565b80633fb9a94e116100e75780633fb9a94e14610246578063490b48f8146102595780634aa4a4fc1461028d5780635955003c146102d957806363a560ec1461030f57610173565b8063087de6f71461018c5780631163b2b0146101ac57806316d8887a146101cc5780632424401f14610213578063258836fe1461022657610173565b3661017357336001600160a01b037f0000000000000000000000007ceb23fd6bc0add59e62ac25578270cff1b9f6191614610171576040516372643aad60e01b815260040160405180910390fd5b005b60405163c6a579c760e01b815260040160405180910390fd5b34801561019857600080fd5b506101716101a7366004613582565b61051b565b3480156101b857600080fd5b506101716101c73660046135b2565b61064b565b3480156101d857600080fd5b506102007f5e17fc5225d4a099df75359ce1f405503ca79498a8dc46a7d583235a0ee45c1681565b6040519081526020015b60405180910390f35b6101716102213660046135d6565b610723565b34801561023257600080fd5b506101716102413660046135ef565b6107a2565b610171610254366004613668565b6108e6565b34801561026557600080fd5b506102007fccc64574297998b6c3edf6078cc5e01268465ff116954e3af02ff3a70a730f4681565b34801561029957600080fd5b506102c17f0000000000000000000000007ceb23fd6bc0add59e62ac25578270cff1b9f61981565b6040516001600160a01b03909116815260200161020a565b3480156102e557600080fd5b506102c16102f43660046135d6565b6003602052600090815260409020546001600160a01b031681565b34801561031b57600080fd5b506102007f3fc733b4d20d27a28452ddf0e9351aced28242fe03389a653cdb783955316b9b81565b34801561034f57600080fd5b5061017161035e3660046136e9565b610956565b610171610371366004613717565b610a13565b34801561038257600080fd5b506103a66103913660046135b2565b60006020819052908152604090205460ff1681565b604051901515815260200161020a565b3480156103c257600080fd5b506102c17f0000000000000000000000007120ca5366d8452d106b56ddd99576a78fa35a1181565b3480156103f657600080fd5b506102c17f00000000000000000000000011984dc4465481512eb5b777e44061c158cf225981565b34801561042a57600080fd5b506102007f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a81565b34801561045e57600080fd5b506102007fe9e995cadb4a71c3f5032f9fbf95a1e1369b940625a86946b14b2d845bf747ab81565b34801561049257600080fd5b506102007f427da25fe773164f88948d3e215c94b6554e2ed5e5f203a821c9f2f6131cf75a81565b3480156104c657600080fd5b506104da6104d5366004613845565b610a63565b60405161020a919061392c565b3480156104f357600080fd5b506102c17f00000000000000000000000069097538190ac1bceaa0c8ebfa7c512b1eb8d24a81565b7f00000000000000000000000069097538190ac1bceaa0c8ebfa7c512b1eb8d24a6001600160a01b031663d33219b46040518163ffffffff1660e01b8152600401602060405180830381865afa158015610579573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061059d919061393f565b6001600160a01b0316336001600160a01b0316146105ce5760405163b909292960e01b815260040160405180910390fd5b6001600160a01b0381166105f55760405163388732eb60e01b815260040160405180910390fd5b60008281526003602052604080822080546001600160a01b0319166001600160a01b038516908117909155905184927f038088a234c0d1d17a5f4c755e87c5283845155cf3344f6c977eedca1275ef1591a35050565b604051632474521560e21b81527fe9e995cadb4a71c3f5032f9fbf95a1e1369b940625a86946b14b2d845bf747ab60048201523360248201527f00000000000000000000000069097538190ac1bceaa0c8ebfa7c512b1eb8d24a6001600160a01b0316906391d1485490604401602060405180830381865afa1580156106d5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106f9919061395c565b61071657604051639c73fc7f60e01b815260040160405180910390fd5b6107208147610db2565b50565b604051632424401f60e01b8152600481018290527f00000000000000000000000011984dc4465481512eb5b777e44061c158cf22596001600160a01b031690632424401f9034906024016000604051808303818588803b15801561078657600080fd5b505af115801561079a573d6000803e3d6000fd5b505050505050565b604051632474521560e21b81527fe9e995cadb4a71c3f5032f9fbf95a1e1369b940625a86946b14b2d845bf747ab60048201523360248201527f00000000000000000000000069097538190ac1bceaa0c8ebfa7c512b1eb8d24a6001600160a01b0316906391d1485490604401602060405180830381865afa15801561082c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610850919061395c565b61086d57604051639c73fc7f60e01b815260040160405180910390fd5b6040516370a0823160e01b81523060048201526108e290839083906001600160a01b038316906370a0823190602401602060405180830381865afa1580156108b9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108dd9190613979565b610e45565b5050565b33301461090657604051633228cc7160e21b815260040160405180910390fd5b61094f85858080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525061094992508691508790506139b5565b83610ea8565b5050505050565b7f00000000000000000000000069097538190ac1bceaa0c8ebfa7c512b1eb8d24a6001600160a01b031663d33219b46040518163ffffffff1660e01b8152600401602060405180830381865afa1580156109b4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109d8919061393f565b6001600160a01b0316336001600160a01b031614610a095760405163b909292960e01b815260040160405180910390fd5b6108e28282611a16565b610a5d848480806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250610a5692508591508690506139b5565b6000610ea8565b50505050565b606060008083806020019051810190610a7c9190613b60565b90925090506000808915610bc9576040516370a0823160e01b81523060048201526001600160a01b038a16906370a0823190602401602060405180830381865afa158015610ace573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610af29190613979565b915089821015610b155760405163018884f160e51b815260040160405180910390fd5b60405180604001604052808a6001600160a01b031681526020018b84610b3b9190613bd9565b90528051600180546001600160a01b039092166001600160a01b031990921691909117905560200151600255610ba68a85600081518110610b7e57610b7e613bf0565b602002602001015185600081518110610b9957610b99613bf0565b6020026020010151611adf565b84600081518110610bb957610bb9613bf0565b6020026020010181935082905250505b604051631fdcd4a760e11b81523090633fb9a94e90610bf090879087908690600401613cca565b600060405180830381600087803b158015610c0a57600080fd5b505af1925050508015610c1b575060015b610d4d578115610cdb57610c50897f0000000000000000000000007120ca5366d8452d106b56ddd99576a78fa35a1184610e45565b60405163e667df2f60e01b81526001600160a01b037f0000000000000000000000007120ca5366d8452d106b56ddd99576a78fa35a11169063e667df2f90610ca8908e908e908e908e908e908c908c90600401613d00565b600060405180830381600087803b158015610cc257600080fd5b505af1158015610cd6573d6000803e3d6000fd5b505050505b6001600080820160006101000a8154906001600160a01b030219169055600182016000905550508663ffffffff168b7f62277c9bd483b2bd832cf3849e52ec0a2b1e4297f133ccb28e963239a8007f5a60008c8e8b604051610d409493929190613d65565b60405180910390a3610d94565b8663ffffffff168b7f62277c9bd483b2bd832cf3849e52ec0a2b1e4297f133ccb28e963239a8007f5a60018c8e8b604051610d8b9493929190613d65565b60405180910390a35b50506040805160208101909152600081529998505050505050505050565b604080516000808252602082019092526001600160a01b038416908390604051610ddc9190613d9e565b60006040518083038185875af1925050503d8060008114610e19576040519150601f19603f3d011682016040523d82523d6000602084013e610e1e565b606091505b5050905080610e40576040516322462d1f60e01b815260040160405180910390fd5b505050565b6040516001600160a01b038316602482015260448101829052610e4090849063a9059cbb60e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152611c0d565b825182518114610ecb576040516305b9af8f60e51b815260040160405180910390fd5b60408051600a808252610160820190925260009182918291816020015b6040805180820190915260008082526020820152815260200190600190039081610ee85750506001549091506001600160a01b031615610f6157604080518082019091526001546001600160a01b03168152600254602082015281518290600090610f5557610f55613bf0565b60200260200101819052505b6000610f6d3447613bd9565b905060005b85811015611a01576000898281518110610f8e57610f8e613bf0565b602002602001015190506000600b811115610fab57610fab613c06565b81600b811115610fbd57610fbd613c06565b03611119576000806000808c8681518110610fda57610fda613bf0565b6020026020010151806020019051810190610ff59190613dba565b935093509350935061100684611ce7565b6000846001600160a01b03166338d52e0f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611046573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061106a919061393f565b90506110768b84611d8e565b9a50611082818a611de0565b985061108f818386611eb9565b61109a818686611eeb565b604051636e553f6560e01b8152600481018590526001600160a01b038481166024830152861690636e553f65906044015b6020604051808303816000875af11580156110ea573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061110e9190613979565b5050505050506119f8565b600181600b81111561112d5761112d613c06565b03611277576000806000808c868151811061114a5761114a613bf0565b60200260200101518060200190518101906111659190613dba565b935093509350935061117684611ce7565b6111808a82611d8e565b99506111ed846001600160a01b03166338d52e0f6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156111c3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111e7919061393f565b89611de0565b604051632d182be560e21b8152600481018590526001600160a01b03848116602483015283811660448301529199509085169063b460af94906064015b6020604051808303816000875af1158015611249573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061126d9190613979565b50505050506119f8565b600281600b81111561128b5761128b613c06565b03611362576000806000808c86815181106112a8576112a8613bf0565b60200260200101518060200190518101906112c39190613dba565b93509350935093506112d484611ce7565b6112de8a82611d8e565b9950611321846001600160a01b031663a919802d6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156111c3573d6000803e3d6000fd5b604051633545906160e21b8152600481018590526001600160a01b03848116602483015283811660448301529199509085169063d51641849060640161122a565b600381600b81111561137657611376613c06565b03611488576000806000808c868151811061139357611393613bf0565b60200260200101518060200190518101906113ae9190613dba565b93509350935093506113bf84611ce7565b6000846001600160a01b031663a919802d6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156113ff573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611423919061393f565b905061142f8b84611d8e565b9a5061143b818a611de0565b9850611448818386611eb9565b611453818686611eeb565b60405163d084b9af60e01b8152600481018590526001600160a01b03848116602483015286169063d084b9af906044016110cb565b600681600b81111561149c5761149c613c06565b036114ee57846114b4576114b18a8a8a611ef6565b94505b60006114da828b85815181106114cc576114cc613bf0565b602002602001015188612221565b90506114e68782611d8e565b9650506119f8565b600781600b81111561150257611502613c06565b0361151757846114b4576114b18a8a8a611ef6565b600881600b81111561152b5761152b613c06565b0361155a5761155389838151811061154557611545613bf0565b6020026020010151876123f8565b95506119f8565b600981600b81111561156e5761156e613c06565b036115965761155389838151811061158857611588613bf0565b6020026020010151876125a2565b600581600b8111156115aa576115aa613c06565b036115fe57816000036115d05760405163b42fccab60e01b815260040160405180910390fd5b6115f48983815181106115e5576115e5613bf0565b60200260200101518786612765565b90965093506119f8565b600481600b81111561161257611612613c06565b036117e65760008060008060008d878151811061163157611631613bf0565b602002602001015180602001905181019061164c9190613e0f565b6040516306a33c7960e51b81526001600160a01b038681166004830152959a50939850919650945092507f00000000000000000000000069097538190ac1bceaa0c8ebfa7c512b1eb8d24a9091169063d4678f2090602401602060405180830381865afa1580156116c1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116e5919061395c565b61170257604051631ddd578f60e11b815260040160405180910390fd5b6001600160a01b038216301461172b5760405163ef34858960e01b815260040160405180910390fd5b611735848a611de0565b985060008061175283600480865161174d9190613bd9565b6129df565b8060200190518101906117659190613b60565b915091506117738282612aec565b604051631f8aa39560e21b8152909d506001600160a01b03881690637e2a8e54906117a8908990899089908990600401613e90565b600060405180830381600087803b1580156117c257600080fd5b505af11580156117d6573d6000803e3d6000fd5b50505050505050505050506119f8565b600a81600b8111156117fa576117fa613c06565b036118eb57600089838151811061181357611813613bf0565b602002602001015180602001905181019061182e9190613979565b9050348114611850576040516379eecdfb60e11b815260040160405180910390fd5b61187a7f0000000000000000000000007ceb23fd6bc0add59e62ac25578270cff1b9f61986611de0565b94507f0000000000000000000000007ceb23fd6bc0add59e62ac25578270cff1b9f6196001600160a01b031663d0e30db0346040518263ffffffff1660e01b81526004016000604051808303818588803b1580156118d757600080fd5b505af115801561110e573d6000803e3d6000fd5b600b81600b8111156118ff576118ff613c06565b036119f8576000808a848151811061191957611919613bf0565b60200260200101518060200190518101906119349190613ec3565b915091506119428882611d8e565b975061196e7f0000000000000000000000007ceb23fd6bc0add59e62ac25578270cff1b9f61987611de0565b604051632e1a7d4d60e01b8152600481018490529096507f0000000000000000000000007ceb23fd6bc0add59e62ac25578270cff1b9f6196001600160a01b031690632e1a7d4d90602401600060405180830381600087803b1580156119d357600080fd5b505af11580156119e7573d6000803e3d6000fd5b505050506119f58183610db2565b50505b50600101610f72565b50611a0c82826130ef565b5050505050505050565b6001600160a01b038216611a3d57604051632cdf6c4b60e11b815260040160405180910390fd5b6001600160a01b03821660009081526020819052604090205481151560ff909116151503611a7e576040516332fe3f1960e21b815260040160405180910390fd5b6001600160a01b03821660008181526020818152604091829020805460ff19168515159081179091558251938452908301527f6bf208b256dacdc89831fd5124ada2aa0f56252c178dd5aff85ee68daf0a7f1c910160405180910390a15050565b8060008084600b811115611af557611af5613c06565b1480611b125750600384600b811115611b1057611b10613c06565b145b15611b8d5760008060008086806020019051810190611b319190613dba565b9350935093509350888314611b8457604080516001600160a01b0380871660208301529181018b905281841660608201529082166080820152929450849260a00160405160208183030381529060405295505b50505050611c05565b600b84600b811115611ba157611ba1613c06565b03611c055760008084806020019051810190611bbd9190613ec3565b91509150868214611c02578192508681604051602001611bf09291909182526001600160a01b0316602082015260400190565b60405160208183030381529060405293505b50505b935093915050565b6000611c62826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b031661322c9092919063ffffffff16565b9050805160001480611c83575080806020019051810190611c83919061395c565b610e405760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084015b60405180910390fd5b604051630c5aed5760e01b81526001600160a01b0382811660048301527f00000000000000000000000069097538190ac1bceaa0c8ebfa7c512b1eb8d24a1690630c5aed5790602401602060405180830381865afa158015611d4d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d71919061395c565b6107205760405163106f25d560e31b815260040160405180910390fd5b60006001600160a01b038316611da5575080611dda565b816001600160a01b0316836001600160a01b031614611dd757604051630727952560e41b815260040160405180910390fd5b50805b92915050565b6060600080611def8585613243565b9150915081611eb05760008115611e1057611e0b826001613ee8565b611e12565b815b6040805180820182526001600160a01b03891680825291516370a0823160e01b8152306004820152929350916020830191906370a0823190602401602060405180830381865afa158015611e6a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e8e9190613979565b815250858281518110611ea357611ea3613bf0565b6020026020010181905250505b50919392505050565b6001600160a01b0382163014801590611eda57506001600160a01b03821633145b15610e4057610e40838330846132a7565b610e408383836132df565b825160009081816001600160401b03811115611f1457611f14613782565b604051908082528060200260200182016040528015611f4757816020015b6060815260200190600190039081611f325790505b50905060005b828110156121ec57858181518110611f6757611f67613bf0565b6020026020010151828281518110611f8157611f81613bf0565b6020026020010181905250806000148015611f9b57508415155b801561200557506000878281518110611fb657611fb6613bf0565b6020026020010151600b811115611fcf57611fcf613c06565b148061200557506003878281518110611fea57611fea613bf0565b6020026020010151600b81111561200357612003613c06565b145b156120ab5760008060008085858151811061202257612022613bf0565b602002602001015180602001905181019061203d9190613dba565b93509350935093508289146120a657604080516001600160a01b0380871660208301529181018b90528184166060820152908216608082015260a00160405160208183030381529060405286868151811061209a5761209a613bf0565b60200260200101819052505b505050505b60068782815181106120bf576120bf613bf0565b6020026020010151600b8111156120d8576120d8613c06565b148061210e575060078782815181106120f3576120f3613bf0565b6020026020010151600b81111561210c5761210c613c06565b145b156121da5760008060008085858151811061212b5761212b613bf0565b60200260200101518060200190518101906121469190613f00565b5050604080516001600160a01b038089166020830152808816828401528616606082015260808101859052600060a0820181905260c0820181905260e08201819052610100808301919091528251808303909101815261012090910190915295995093975091955093506121b8915050565b8686815181106121ca576121ca613bf0565b6020026020010181905250505050505b806121e481613f8c565b915050611f4d565b508581604051602001612200929190613fa5565b60405160208183030381529060405280519060200120925050509392505050565b6040805161010081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e08101829052838060200190518101906122759190613f00565b60e089015260c088015260ff1660a0870152608086015260608501526001600160a01b0390811660408501529081166020840152168082526122b690611ce7565b600685600b8111156122ca576122ca613c06565b036123605780600001516001600160a01b0316630fad31318260200151836040015184606001518560800151888760a001518860c001518960e001516040518963ffffffff1660e01b8152600401612329989796959493929190613fd3565b600060405180830381600087803b15801561234357600080fd5b505af1158015612357573d6000803e3d6000fd5b505050506123ec565b600785600b81111561237457612374613c06565b036123d35780600001516001600160a01b03166396ebbc458260200151836040015184606001518560800151888760a001518860c001518960e001516040518963ffffffff1660e01b8152600401612329989796959493929190613fd3565b604051632f67478960e21b815260040160405180910390fd5b60200151949350505050565b600080600080600080600088806020019051810190612417919061401a565b955095509550955095509550600061242f8984611d8e565b905061243c858386611eb9565b612467857f00000000000000000000000011984dc4465481512eb5b777e44061c158cf225986611eeb565b6040516345560b5d60e11b815263ffffffff881660048201526001600160a01b038481166024830152868116604483015282811660648301526084820186905260a4820188905260e060c4830152600060e48301819052917f00000000000000000000000011984dc4465481512eb5b777e44061c158cf225990911690638aac16ba90610104016020604051808303816000875af115801561250d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125319190613979565b604080518a81526001600160a01b038981166020830152918101889052608060608201819052600090820152919250851690339083907fb7c6eb876c99dadc81a4df296037aa08e5c6be07a3765524cf8812aec701f7e49060a00160405180910390a4509998505050505050505050565b6000806000806000806000888060200190518101906125c19190614083565b955095509550955095509550600080828060200190518101906125e491906140f5565b50915091506125fc8a6125f78484612aec565b611d8e565b9850612609868587611eb9565b612634867f00000000000000000000000011984dc4465481512eb5b777e44061c158cf225987611eeb565b60007f00000000000000000000000011984dc4465481512eb5b777e44061c158cf22596001600160a01b0316638aac16ba8a600360008d815260200190815260200160002060009054906101000a90046001600160a01b03168a8e8b8e8b6040518863ffffffff1660e01b81526004016126b49796959493929190614161565b6020604051808303816000875af11580156126d3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126f79190613979565b60008a815260036020526040908190205490519192506001600160a01b031690339083907fb7c6eb876c99dadc81a4df296037aa08e5c6be07a3765524cf8812aec701f7e49061274e908e908d908d908c906141bc565b60405180910390a450505050505050505092915050565b600060606000806000806000806000808c80602001905181019061278991906141e9565b975097509750975097509750975097507f00000000000000000000000069097538190ac1bceaa0c8ebfa7c512b1eb8d24a6001600160a01b031663877945f0896040518263ffffffff1660e01b81526004016127f491906001600160a01b0391909116815260200190565b602060405180830381865afa158015612811573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612835919061395c565b6128525760405163418f824160e01b815260040160405180910390fd5b61285c878c611de0565b9a50612868868c611de0565b9a50612875878987611eeb565b6001600160a01b038316301480159061291557506040516306a33c7960e51b81526001600160a01b0384811660048301527f00000000000000000000000069097538190ac1bceaa0c8ebfa7c512b1eb8d24a169063d4678f2090602401602060405180830381865afa1580156128ef573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612913919061395c565b155b15612927576129248c84611d8e565b9b505b6001600160a01b0382163014612944576129418c83611d8e565b9b505b60405163e2c4f23b60e01b81526001600160a01b03888116600483015287811660248301526044820187905260648201869052848116608483015283811660a483015260c4820183905289169063e2c4f23b9060e401600060405180830381600087803b1580156129b457600080fd5b505af11580156129c8573d6000803e3d6000fd5b509d9f9c9e509b9c50505050505050505050505050565b6060816129ed81601f613ee8565b1015612a2c5760405162461bcd60e51b815260206004820152600e60248201526d736c6963655f6f766572666c6f7760901b6044820152606401611cde565b612a368284613ee8565b84511015612a7a5760405162461bcd60e51b8152602060048201526011602482015270736c6963655f6f75744f66426f756e647360781b6044820152606401611cde565b606082158015612a995760405191506000825260208201604052612ae3565b6040519150601f8416801560200281840101858101878315602002848b0101015b81831015612ad2578051835260209283019201612aba565b5050858452601f01601f1916604052505b50949350505050565b60008083600081518110612b0257612b02613bf0565b6020026020010151600b811115612b1b57612b1b613c06565b1480612b525750600383600081518110612b3757612b37613bf0565b6020026020010151600b811115612b5057612b50613c06565b145b15612b9357600082600081518110612b6c57612b6c613bf0565b6020026020010151806020019051810190612b879190613dba565b509350611dda92505050565b600183600081518110612ba857612ba8613bf0565b6020026020010151600b811115612bc157612bc1613c06565b1480612bf85750600283600081518110612bdd57612bdd613bf0565b6020026020010151600b811115612bf657612bf6613c06565b145b15612c3957600082600081518110612c1257612c12613bf0565b6020026020010151806020019051810190612c2d9190613dba565b9450611dda9350505050565b600b83600081518110612c4e57612c4e613bf0565b6020026020010151600b811115612c6757612c67613c06565b03612ca657600082600081518110612c8157612c81613bf0565b6020026020010151806020019051810190612c9c9190613ec3565b9250611dda915050565b600783600081518110612cbb57612cbb613bf0565b6020026020010151600b811115612cd457612cd4613c06565b1480612d0b5750600683600081518110612cf057612cf0613bf0565b6020026020010151600b811115612d0957612d09613c06565b145b15612d5157600082600081518110612d2557612d25613bf0565b6020026020010151806020019051810190612d409190613f00565b50949750611dda9650505050505050565b600483600081518110612d6657612d66613bf0565b6020026020010151600b811115612d7f57612d7f613c06565b03612dfb57600082600081518110612d9957612d99613bf0565b6020026020010151806020019051810190612db49190613e0f565b945050505050600080612dd083600480865161174d9190613bd9565b806020019051810190612de39190613b60565b91509150612df18282612aec565b9350505050611dda565b600883600081518110612e1057612e10613bf0565b6020026020010151600b811115612e2957612e29613c06565b03612e6c57600082600081518110612e4357612e43613bf0565b6020026020010151806020019051810190612e5e919061401a565b509550611dda945050505050565b600983600081518110612e8157612e81613bf0565b6020026020010151600b811115612e9a57612e9a613c06565b03612efb57600082600081518110612eb457612eb4613bf0565b6020026020010151806020019051810190612ecf919061427c565b94505050505060008082806020019051810190612eec91906140f5565b5091509150612df18282612aec565b600a83600081518110612f1057612f10613bf0565b6020026020010151600b811115612f2957612f29613c06565b036130a35782516000612f3d600183613bd9565b6001600160401b03811115612f5457612f54613782565b604051908082528060200260200182016040528015612f7d578160200160208202803683370190505b5090506000612f8d600184613bd9565b6001600160401b03811115612fa457612fa4613782565b604051908082528060200260200182016040528015612fd757816020015b6060815260200190600190039081612fc25790505b50905060015b8381101561309857868181518110612ff757612ff7613bf0565b60200260200101518360018361300d9190613bd9565b8151811061301d5761301d613bf0565b6020026020010190600b81111561303657613036613c06565b9081600b81111561304957613049613c06565b8152505085818151811061305f5761305f613bf0565b6020026020010151826001836130759190613bd9565b8151811061308557613085613bf0565b6020908102919091010152600101612fdd565b50612df18282612aec565b6005836000815181106130b8576130b8613bf0565b6020026020010151600b8111156130d1576130d1613c06565b03611dda5760405163b42fccab60e01b815260040160405180910390fd5b815160005b8181101561320b5760006001600160a01b031684828151811061311957613119613bf0565b6020026020010151600001516001600160a01b03161461320357600084828151811061314757613147613bf0565b6020026020010151602001519050600085838151811061316957613169613bf0565b6020908102919091010151516040516370a0823160e01b81523060048201526001600160a01b03909116906370a0823190602401602060405180830381865afa1580156131ba573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131de9190613979565b905081811461320057604051632c1b2f5f60e21b815260040160405180910390fd5b50505b6001016130f4565b50478214610e4057604051632c1b2f5f60e21b815260040160405180910390fd5b606061323b84846000856133f4565b949350505050565b80516000908190815b8181101561329e5784818151811061326657613266613bf0565b6020026020010151600001516001600160a01b0316866001600160a01b031603613296576001935080925061329e565b60010161324c565b50509250929050565b6040516001600160a01b0380851660248301528316604482015260648101829052610a5d9085906323b872dd60e01b90608401610e71565b8015806133595750604051636eb1769f60e11b81523060048201526001600160a01b03838116602483015284169063dd62ed3e90604401602060405180830381865afa158015613333573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133579190613979565b155b6133c45760405162461bcd60e51b815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527520746f206e6f6e2d7a65726f20616c6c6f77616e636560501b6064820152608401611cde565b6040516001600160a01b038316602482015260448101829052610e4090849063095ea7b360e01b90606401610e71565b6060824710156134555760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401611cde565b600080866001600160a01b031685876040516134719190613d9e565b60006040518083038185875af1925050503d80600081146134ae576040519150601f19603f3d011682016040523d82523d6000602084013e6134b3565b606091505b50915091506134c4878383876134cf565b979650505050505050565b6060831561353e578251600003613537576001600160a01b0385163b6135375760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401611cde565b508161323b565b61323b83838151156135535781518083602001fd5b8060405162461bcd60e51b8152600401611cde919061392c565b6001600160a01b038116811461072057600080fd5b6000806040838503121561359557600080fd5b8235915060208301356135a78161356d565b809150509250929050565b6000602082840312156135c457600080fd5b81356135cf8161356d565b9392505050565b6000602082840312156135e857600080fd5b5035919050565b6000806040838503121561360257600080fd5b823561360d8161356d565b915060208301356135a78161356d565b60008083601f84011261362f57600080fd5b5081356001600160401b0381111561364657600080fd5b6020830191508360208260051b850101111561366157600080fd5b9250929050565b60008060008060006060868803121561368057600080fd5b85356001600160401b038082111561369757600080fd5b6136a389838a0161361d565b909750955060208801359150808211156136bc57600080fd5b506136c98882890161361d565b96999598509660400135949350505050565b801515811461072057600080fd5b600080604083850312156136fc57600080fd5b82356137078161356d565b915060208301356135a7816136db565b6000806000806040858703121561372d57600080fd5b84356001600160401b038082111561374457600080fd5b6137508883890161361d565b9096509450602087013591508082111561376957600080fd5b506137768782880161361d565b95989497509550505050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b03811182821017156137c0576137c0613782565b604052919050565b60006001600160401b038211156137e1576137e1613782565b50601f01601f191660200190565b600082601f83011261380057600080fd5b813561381361380e826137c8565b613798565b81815284602083860101111561382857600080fd5b816020850160208301376000918101602001919091529392505050565b60008060008060008060c0878903121561385e57600080fd5b863595506020870135945060408701356138778161356d565b935060608701356138878161356d565b9250608087013563ffffffff811681146138a057600080fd5b915060a08701356001600160401b038111156138bb57600080fd5b6138c789828a016137ef565b9150509295509295509295565b60005b838110156138ef5781810151838201526020016138d7565b83811115610a5d5750506000910152565b600081518084526139188160208601602086016138d4565b601f01601f19169290920160200192915050565b6020815260006135cf6020830184613900565b60006020828403121561395157600080fd5b81516135cf8161356d565b60006020828403121561396e57600080fd5b81516135cf816136db565b60006020828403121561398b57600080fd5b5051919050565b60006001600160401b038211156139ab576139ab613782565b5060051b60200190565b60006139c361380e84613992565b80848252602080830192508560051b8501368111156139e157600080fd5b855b81811015613a1c5780356001600160401b03811115613a025760008081fd5b613a0e36828a016137ef565b8652509382019382016139e3565b50919695505050505050565b600082601f830112613a3957600080fd5b81516020613a4961380e83613992565b82815260059290921b84018101918181019086841115613a6857600080fd5b8286015b84811015613a91578051600c8110613a845760008081fd5b8352918301918301613a6c565b509695505050505050565b600082601f830112613aad57600080fd5b8151613abb61380e826137c8565b818152846020838601011115613ad057600080fd5b61323b8260208301602087016138d4565b600082601f830112613af257600080fd5b81516020613b0261380e83613992565b82815260059290921b84018101918181019086841115613b2157600080fd5b8286015b84811015613a915780516001600160401b03811115613b445760008081fd5b613b528986838b0101613a9c565b845250918301918301613b25565b60008060408385031215613b7357600080fd5b82516001600160401b0380821115613b8a57600080fd5b613b9686838701613a28565b93506020850151915080821115613bac57600080fd5b50613bb985828601613ae1565b9150509250929050565b634e487b7160e01b600052601160045260246000fd5b600082821015613beb57613beb613bc3565b500390565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052602160045260246000fd5b60008151808452602080850194508084016000805b84811015613c69578251600c8110613c5757634e487b7160e01b83526021600452602483fd5b88529683019691830191600101613c31565b50959695505050505050565b600081518084526020808501808196508360051b8101915082860160005b85811015613cbd578284038952613cab848351613900565b98850198935090840190600101613c93565b5091979650505050505050565b606081526000613cdd6060830186613c1c565b8281036020840152613cef8186613c75565b915050826040830152949350505050565b878152602081018790526001600160a01b0386811660408301528516606082015263ffffffff8416608082015260e060a08201819052600090613d4590830185613c1c565b82810360c0840152613d578185613c75565b9a9950505050505050505050565b841515815260018060a01b0384166020820152826040820152608060608201526000613d946080830184613900565b9695505050505050565b60008251613db08184602087016138d4565b9190910192915050565b60008060008060808587031215613dd057600080fd5b8451613ddb8161356d565b602086015160408701519195509350613df38161356d565b6060860151909250613e048161356d565b939692955090935050565b600080600080600060a08688031215613e2757600080fd5b8551613e328161356d565b6020870151909550613e438161356d565b604087015160608801519195509350613e5b8161356d565b60808701519092506001600160401b03811115613e7757600080fd5b613e8388828901613a9c565b9150509295509295909350565b6001600160a01b0385811682526020820185905283166040820152608060608201819052600090613d9490830184613900565b60008060408385031215613ed657600080fd5b8251915060208301516135a78161356d565b60008219821115613efb57613efb613bc3565b500190565b600080600080600080600080610100898b031215613f1d57600080fd5b8851613f288161356d565b60208a0151909850613f398161356d565b60408a0151909750613f4a8161356d565b80965050606089015194506080890151935060a089015160ff81168114613f7057600080fd5b60c08a015160e0909a0151989b979a5095989497939692505050565b600060018201613f9e57613f9e613bc3565b5060010190565b604081526000613fb86040830185613c1c565b8281036020840152613fca8185613c75565b95945050505050565b6001600160a01b03988916815296909716602087015260408601949094526060850192909252608084015260ff1660a083015260c082015260e08101919091526101000190565b60008060008060008060c0878903121561403357600080fd5b8651955060208701519450604087015161404c8161356d565b6060880151608089015191955093506140648161356d565b60a08801519092506140758161356d565b809150509295509295509295565b60008060008060008060c0878903121561409c57600080fd5b865195506020870151945060408701516140b58161356d565b6060880151608089015191955093506140cd8161356d565b60a08801519092506001600160401b038111156140e957600080fd5b6138c789828a01613a9c565b60008060006060848603121561410a57600080fd5b83516001600160401b038082111561412157600080fd5b61412d87838801613a28565b9450602086015191508082111561414357600080fd5b5061415086828701613ae1565b925050604084015190509250925092565b63ffffffff881681526001600160a01b0387811660208301528681166040830152851660608201526080810184905260a0810183905260e060c082018190526000906141af90830184613900565b9998505050505050505050565b84815260018060a01b0384166020820152826040820152608060608201526000613d946080830184613900565b600080600080600080600080610100898b03121561420657600080fd5b88516142118161356d565b60208a01519098506142228161356d565b60408a01519097506142338161356d565b80965050606089015194506080890151935060a08901516142538161356d565b60c08a01519093506142648161356d565b8092505060e089015190509295985092959890939650565b600080600080600060a0868803121561429457600080fd5b855194506020860151935060408601516142ad8161356d565b6060870151608088015191945092506001600160401b03811115613e7757600080fdfea2646970667358221220895d0472be94d1dce0b168504cb164620e17c452cff2b751dc482ac28487208264736f6c634300080f0033

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

0000000000000000000000007ceb23fd6bc0add59e62ac25578270cff1b9f61900000000000000000000000011984dc4465481512eb5b777e44061c158cf225900000000000000000000000069097538190ac1bceaa0c8ebfa7c512b1eb8d24a

-----Decoded View---------------
Arg [0] : weth (address): 0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619
Arg [1] : connext_ (address): 0x11984dc4465481512eb5b777E44061C158CF2259
Arg [2] : chief (address): 0x69097538190AC1bceAa0C8ebFA7C512b1eb8D24a

-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 0000000000000000000000007ceb23fd6bc0add59e62ac25578270cff1b9f619
Arg [1] : 00000000000000000000000011984dc4465481512eb5b777e44061c158cf2259
Arg [2] : 00000000000000000000000069097538190ac1bceaa0c8ebfa7c512b1eb8d24a


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.