ETH Price: $2,808.98 (+1.40%)

Contract

0x5AB3e51608cEa26090445CA89bc91628C8bB99f9
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To

There are no matching entries

> 10 Token Transfers found.

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
OperationExecutor

Compiler Version
v0.8.15+commit.e14f2714

Optimization Enabled:
No with 200 runs

Other Settings:
default evmVersion
File 1 of 22 : OperationExecutor.sol
//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.15;

import { ServiceRegistry } from "./ServiceRegistry.sol";
import { OperationStorage } from "./OperationStorage.sol";
import { OperationsRegistry } from "./OperationsRegistry.sol";
import { DSProxy } from "../libs/DS/DSProxy.sol";
import { ActionAddress } from "../libs/ActionAddress.sol";
import { TakeFlashloan } from "../actions/common/TakeFlashloan.sol";
import { Executable } from "../actions/common/Executable.sol";
import { IERC3156FlashBorrower } from "../interfaces/flashloan/IERC3156FlashBorrower.sol";
import { IERC3156FlashLender } from "../interfaces/flashloan/IERC3156FlashLender.sol";
import { SafeERC20, IERC20 } from "../libs/SafeERC20.sol";
import { SafeMath } from "../libs/SafeMath.sol";
import { FlashloanData, Call } from "./types/Common.sol";
import { OPERATION_STORAGE, OPERATIONS_REGISTRY, OPERATION_EXECUTOR } from "./constants/Common.sol";
import { FLASH_MINT_MODULE } from "./constants/Maker.sol";

/**
 * @title Operation Executor
 * @notice Is responsible for executing sequences of Actions (Operations)
 */
contract OperationExecutor is IERC3156FlashBorrower {
  using ActionAddress for address;
  using SafeERC20 for IERC20;
  using SafeMath for uint256;

  ServiceRegistry public immutable registry;

  /**
   * @dev Emitted once an Operation has completed execution
   * @param name The address initiating the deposit
   * @param calls An array of Action calls the operation must execute
   **/
  event Operation(string name, Call[] calls);

  constructor(ServiceRegistry _registry) {
    registry = _registry;
  }

  /**
   * @notice Executes an operation
   * @dev
   * There are operations stored in the OperationsRegistry which guarantee the order of execution of actions for a given Operation.
   * There is a possibility to execute an arrays of calls that don't form an official operation.
   *
   * Operation storage is cleared before and after an operation is executed.
   *
   * To avoid re-entrancy attack, there is a lock implemented on OpStorage.
   * A standard reentrancy modifier is not sufficient because the second call via the onFlashloan handler
   * calls aggregateCallback via DSProxy once again but this breaks the special modifier _ behaviour
   * and the modifier cannot return the execution flow to the original function.
   * This is why re-entrancy defence is immplemented here using an external storage contract via the lock/unlock functions
   * @param calls An array of Action calls the operation must execute
   * @param operationName The name of the Operation being executed
   */
  function executeOp(Call[] memory calls, string calldata operationName) public payable {
    OperationStorage opStorage = OperationStorage(registry.getRegisteredService(OPERATION_STORAGE));
    opStorage.lock();
    OperationsRegistry opRegistry = OperationsRegistry(
      registry.getRegisteredService(OPERATIONS_REGISTRY)
    );

    opStorage.clearStorage();
    opStorage.setOperationActions(opRegistry.getOperation(operationName));
    aggregate(calls);

    opStorage.clearStorage();
    opStorage.unlock();
    emit Operation(operationName, calls);
  }

  function aggregate(Call[] memory calls) internal {
    OperationStorage opStorage = OperationStorage(registry.getRegisteredService(OPERATION_STORAGE));
    bool hasActionsToVerify = opStorage.hasActionsToVerify();
    for (uint256 current = 0; current < calls.length; current++) {
      if (hasActionsToVerify) {
        opStorage.verifyAction(calls[current].targetHash);
      }

      address target = registry.getServiceAddress(calls[current].targetHash);
      target.execute(calls[current].callData);
    }
  }

  /**
   * @notice Not to be called directly
   * @dev Is called by the Operation Executor via a user's proxy to execute Actions nested in the FlashloanAction
   * @param calls An array of Action calls the operation must execute
   */
  function callbackAggregate(Call[] memory calls) external {
    require(
      msg.sender == registry.getRegisteredService(OPERATION_EXECUTOR),
      "OpExecutor: Caller forbidden"
    );
    aggregate(calls);
  }

  /**
   * @notice Not to be called directly.
   * @dev Callback handler for use by a flashloan lender contract.
   * If the dsProxyFlashloan flag is supplied we reestablish the calling context as the user's proxy (at time of writing DSProxy). Although stored values will
   * We set the initiator on Operation Storage such that calls originating from other contracts EG Oasis Automation Bot (see https://github.com/OasisDEX/automation-smartcontracts)
   * The initiator address will be used to store values against the original msg.sender.
   * This protects against the Operation Storage values being polluted by malicious code from untrusted 3rd party contracts.

   * @param initiator Is the address of the contract that initiated the flashloan (EG Operation Executor)
   * @param asset The address of the asset being flash loaned
   * @param amount The size of the flash loan
   * @param fee The Fee charged for the loan
   * @param data Any calldata sent to the contract for execution later in the callback
   */
  function onFlashLoan(
    address initiator,
    address asset,
    uint256 amount,
    uint256 fee,
    bytes calldata data
  ) external override returns (bytes32) {
    address lender = registry.getRegisteredService(FLASH_MINT_MODULE);

    require(msg.sender == lender, "Untrusted flashloan lender");

    FlashloanData memory flData = abi.decode(data, (FlashloanData));

    require(IERC20(asset).balanceOf(address(this)) >= flData.amount, "Flashloan inconsistency");

    if (flData.dsProxyFlashloan) {
      IERC20(asset).safeTransfer(initiator, flData.amount);

      DSProxy(payable(initiator)).execute(
        address(this),
        abi.encodeWithSelector(this.callbackAggregate.selector, flData.calls)
      );
    } else {
      OperationStorage opStorage = OperationStorage(
        registry.getRegisteredService(OPERATION_STORAGE)
      );
      opStorage.setInitiator(initiator);
      aggregate(flData.calls);
    }

    uint256 paybackAmount = amount.add(fee);
    require(
      IERC20(asset).balanceOf(address(this)) >= paybackAmount,
      "Insufficient funds for payback"
    );

    IERC20(asset).safeApprove(lender, paybackAmount);

    return keccak256("ERC3156FlashBorrower.onFlashLoan");
  }
}

File 2 of 22 : ServiceRegistry.sol
//SPDX-License-Identifier: Unlicense
pragma solidity >=0.8.1;

/**
 * @title Service Registry
 * @notice Stores addresses of deployed contracts
 */
contract ServiceRegistry {
  uint256 public constant MAX_DELAY = 30 days;

  mapping(bytes32 => uint256) public lastExecuted;
  mapping(bytes32 => address) private namedService;
  address public owner;
  uint256 public requiredDelay;

  modifier validateInput(uint256 len) {
    require(msg.data.length == len, "registry/illegal-padding");
    _;
  }

  modifier delayedExecution() {
    bytes32 operationHash = keccak256(msg.data);
    uint256 reqDelay = requiredDelay;

    /* solhint-disable not-rely-on-time */
    if (lastExecuted[operationHash] == 0 && reqDelay > 0) {
      // not called before, scheduled for execution
      lastExecuted[operationHash] = block.timestamp;
      emit ChangeScheduled(operationHash, block.timestamp + reqDelay, msg.data);
    } else {
      require(block.timestamp - reqDelay > lastExecuted[operationHash], "registry/delay-too-small");
      emit ChangeApplied(operationHash, block.timestamp, msg.data);
      _;
      lastExecuted[operationHash] = 0;
    }
    /* solhint-enable not-rely-on-time */
  }

  modifier onlyOwner() {
    require(msg.sender == owner, "registry/only-owner");
    _;
  }

  constructor(uint256 initialDelay) {
    require(initialDelay <= MAX_DELAY, "registry/invalid-delay");
    requiredDelay = initialDelay;
    owner = msg.sender;
  }

  /**
   * @param newOwner Transfers ownership of the registry to a new address
   */
  function transferOwnership(address newOwner)
    external
    onlyOwner
    validateInput(36)
    delayedExecution
  {
    owner = newOwner;
  }

  /**
   * @param newDelay Updates the required delay before an change can be confirmed with a follow up t/x
   */
  function changeRequiredDelay(uint256 newDelay)
    external
    onlyOwner
    validateInput(36)
    delayedExecution
  {
    require(newDelay <= MAX_DELAY, "registry/invalid-delay");
    requiredDelay = newDelay;
  }

  /**
   * @param name Hashes the supplied name
   * @return Returns the hash of the name
   */
  function getServiceNameHash(string memory name) external pure returns (bytes32) {
    return keccak256(abi.encodePacked(name));
  }

  /**
   * @param serviceNameHash The hashed name
   * @param serviceAddress The address stored for a given name
   */
  function addNamedService(bytes32 serviceNameHash, address serviceAddress)
    external
    onlyOwner
    validateInput(68)
    delayedExecution
  {
    require(namedService[serviceNameHash] == address(0), "registry/service-override");
    namedService[serviceNameHash] = serviceAddress;
  }

  /**
   * @param serviceNameHash The hashed name
   * @param serviceAddress The address to update for a given name
   */
  function updateNamedService(bytes32 serviceNameHash, address serviceAddress)
    external
    onlyOwner
    validateInput(68)
    delayedExecution
  {
    require(namedService[serviceNameHash] != address(0), "registry/service-does-not-exist");
    namedService[serviceNameHash] = serviceAddress;
  }

  /**
   * @param serviceNameHash The hashed service name to remove
   */
  function removeNamedService(bytes32 serviceNameHash) external onlyOwner validateInput(36) {
    require(namedService[serviceNameHash] != address(0), "registry/service-does-not-exist");
    namedService[serviceNameHash] = address(0);
    emit NamedServiceRemoved(serviceNameHash);
  }

  /**
   * @param serviceName Get a service address by its name
   */
  function getRegisteredService(string memory serviceName) external view returns (address) {
    return namedService[keccak256(abi.encodePacked(serviceName))];
  }

  /**
   * @param serviceNameHash Get a service address by the hash of its name
   */
  function getServiceAddress(bytes32 serviceNameHash) external view returns (address) {
    return namedService[serviceNameHash];
  }

  /**
   * @dev Voids any submitted changes that are yet to be confirmed by a follow-up transaction
   * @param scheduledExecution Clear any scheduled changes
   */
  function clearScheduledExecution(bytes32 scheduledExecution)
    external
    onlyOwner
    validateInput(36)
  {
    require(lastExecuted[scheduledExecution] > 0, "registry/execution-not-scheduled");
    lastExecuted[scheduledExecution] = 0;
    emit ChangeCancelled(scheduledExecution);
  }

  event ChangeScheduled(bytes32 dataHash, uint256 scheduledFor, bytes data);
  event ChangeApplied(bytes32 dataHash, uint256 appliedAt, bytes data);
  event ChangeCancelled(bytes32 dataHash);
  event NamedServiceRemoved(bytes32 nameHash);
}

File 3 of 22 : OperationStorage.sol
pragma solidity ^0.8.15;

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

/**
 * @title Operation Storage
 * @notice Stores the return values from Actions during an Operation's execution
 * @dev valuesHolders is an array of t/x initiators (msg.sender) who have pushed values to Operation Storage
 * returnValues is a mapping between a msg.sender and an array of Action return values generated by that senders transaction
 */
contract OperationStorage {
  uint8 internal action = 0;
  bytes32[] public actions;
  mapping(address => bytes32[]) public returnValues;
  address[] public valuesHolders;
  bool private locked;
  address private whoLocked;
  address public initiator;
  address immutable operationExecutorAddress;

  ServiceRegistry internal immutable registry;

  constructor(ServiceRegistry _registry, address _operationExecutorAddress) {
    registry = _registry;
    operationExecutorAddress = _operationExecutorAddress;
  }

  /**
   * @dev Locks storage to protect against re-entrancy attacks.@author
   */
  function lock() external {
    locked = true;
    whoLocked = msg.sender;
  }

  /**
   * @dev Only the original locker can unlock the contract at the end of the transaction
   */
  function unlock() external {
    require(whoLocked == msg.sender, "Only the locker can unlock");
    require(locked, "Not locked");
    locked = false;
    whoLocked = address(0);
  }

  /**
   * @dev Sets the initiator of the original call
   * Is used by Automation Bot branch in the onFlashloan callback in Operation Executor
   * Ensures that third party calls to Operation Storage do not maliciously override values in Operation Storage
   * @param _initiator Sets the initiator to Operation Executor contract when storing return values from flashloan nested Action
   */
  function setInitiator(address _initiator) external {
    require(msg.sender == operationExecutorAddress);
    initiator = _initiator;
  }

  /**
   * @param _actions Stores the Actions currently being executed for a given Operation
   */
  function setOperationActions(bytes32[] memory _actions) external {
    actions = _actions;
  }

  /**
   * @param actionHash Checks the current action has against the expected action hash
   */
  function verifyAction(bytes32 actionHash) external {
    require(actions[action] == actionHash, "incorrect-action");
    registry.getServiceAddress(actionHash);
    action++;
  }

  /**
   * @dev Custom operations have no Actions stored in Operation Registry
   * @return Returns true / false depending on whether the Operation has any actions to verify the Operation against
   */
  function hasActionsToVerify() external view returns (bool) {
    return actions.length > 0;
  }

  /**
   * @param value Pushes a bytes32 to end of the returnValues array
   */
  function push(bytes32 value) external {
    address who = msg.sender;
    if (who == operationExecutorAddress) {
      who = initiator;
    }

    if (returnValues[who].length == 0) {
      valuesHolders.push(who);
    }
    returnValues[who].push(value);
  }

  /**
    * @dev Values are stored against an address (who)
    * This ensures that malicious actors looking to push values to Operation Storage mid transaction cannot overwrite values
   * @param index The index of the desired value
   * @param who The msg.sender address responsible for storing values
   */
  function at(uint256 index, address who) external view returns (bytes32) {
    if (who == operationExecutorAddress) {
      who = initiator;
    }
    return returnValues[who][index];
  }

  /**
   * @param who The msg.sender address responsible for storing values
   * @return The length of return values stored against a given msg.sender address
   */
  function len(address who) external view returns (uint256) {
    if (who == operationExecutorAddress) {
      who = initiator;
    }
    return returnValues[who].length;
  }

  /**
   * @dev Clears storage in preparation for the next Operation
   */
  function clearStorage() external {
    delete action;
    delete actions;
    for (uint256 i = 0; i < valuesHolders.length; i++) {
      delete returnValues[valuesHolders[i]];
    }
    delete valuesHolders;
  }
}

File 4 of 22 : OperationsRegistry.sol
pragma solidity ^0.8.15;

import { Operation } from "./types/Common.sol";
import { OPERATIONS_REGISTRY } from "./constants/Common.sol";

struct StoredOperation {
  bytes32[] actions;
  string name;
}

/**
 * @title Operation Registry
 * @notice Stores the Actions that constitute a given Operation
 */
contract OperationsRegistry {
  mapping(string => StoredOperation) private operations;
  address public owner;

  modifier onlyOwner() {
    require(msg.sender == owner, "only-owner");
    _;
  }

  constructor() {
    owner = msg.sender;
  }

  /**
   * @notice Stores the Actions that constitute a given Operation
   * @param newOwner The address of the new owner of the Operations Registry
   */
  function transferOwnership(address newOwner) public onlyOwner {
    owner = newOwner;
  }

  /**
   * @dev Emitted when a new operation is added or an existing operation is updated
   * @param name The Operation name
   **/
  event OperationAdded(string name);

  /**
   * @notice Adds an Operation's Actions keyed to a an operation name
   * @param name The Operation name
   * @param actions An array the Actions the Operation consists of
   */
  function addOperation(string memory name, bytes32[] memory actions) external onlyOwner {
    operations[name] = StoredOperation(actions, name);
    emit OperationAdded(name);
  }

  /**
   * @notice Gets an Operation from the Registry
   * @param name The name of the Operation
   * @return actions Returns an array of Actions
   */
  function getOperation(string memory name) external view returns (bytes32[] memory actions) {
    if (keccak256(bytes(operations[name].name)) == keccak256(bytes(""))) {
      revert("Operation doesn't exist");
    }
    actions = operations[name].actions;
  }
}

File 5 of 22 : DSProxy.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.15;

import "./DSAuth.sol";
import "./DSNote.sol";

abstract contract DSProxy is DSAuth, DSNote {
  DSProxyCache public cache; // global cache for contracts

  constructor(address _cacheAddr) {
    require(setCache(_cacheAddr), "Cache not set");
  }

  // solhint-disable-next-line no-empty-blocks
  receive() external payable {}

  // use the proxy to execute calldata _data on contract _code
  function execute(bytes memory _code, bytes memory _data)
    public
    payable
    virtual
    returns (address target, bytes32 response);

  function execute(address _target, bytes memory _data)
    public
    payable
    virtual
    returns (bytes32 response);

  //set new cache
  function setCache(address _cacheAddr) public payable virtual returns (bool);
}

contract DSProxyCache {
  mapping(bytes32 => address) cache;

  function read(bytes memory _code) public view returns (address) {
    bytes32 hash = keccak256(_code);
    return cache[hash];
  }

  function write(bytes memory _code) public returns (address target) {
    assembly {
      target := create(0, add(_code, 0x20), mload(_code))
      switch iszero(extcodesize(target))
      case 1 {
        // throw if contract failed to deploy
        revert(0, 0)
      }
    }
    bytes32 hash = keccak256(_code);
    cache[hash] = target;
  }
}

File 6 of 22 : ActionAddress.sol
pragma solidity ^0.8.15;
import "./Address.sol";
import "../actions/common/Executable.sol";

library ActionAddress {
  using Address for address;

  function execute(address action, bytes memory callData) internal {
    require(isCallingAnExecutable(callData), "OpExecutor: illegal call");
    action.functionDelegateCall(callData, "OpExecutor: low-level delegatecall failed");
  }

  function isCallingAnExecutable(bytes memory callData) private pure returns (bool) {
    bytes4 executeSelector = convertBytesToBytes4(
      abi.encodeWithSelector(Executable.execute.selector)
    );
    bytes4 selector = convertBytesToBytes4(callData);
    return selector == executeSelector;
  }

  function convertBytesToBytes4(bytes memory inBytes) private pure returns (bytes4 outBytes4) {
    if (inBytes.length == 0) {
      return 0x0;
    }

    assembly {
      outBytes4 := mload(add(inBytes, 32))
    }
  }
}

File 7 of 22 : TakeFlashloan.sol
pragma solidity ^0.8.15;

import { Executable } from "../common/Executable.sol";
import { ServiceRegistry } from "../../core/ServiceRegistry.sol";
import { IERC3156FlashBorrower } from "../../interfaces/flashloan/IERC3156FlashBorrower.sol";
import { IERC3156FlashLender } from "../../interfaces/flashloan/IERC3156FlashLender.sol";
import { FlashloanData } from "../../core/types/Common.sol";
import { OPERATION_EXECUTOR, DAI, TAKE_FLASH_LOAN_ACTION } from "../../core/constants/Common.sol";
import { FLASH_MINT_MODULE } from "../../core/constants/Maker.sol";
import { ProxyPermission } from "../../libs/DS/ProxyPermission.sol";

/**
 * @title TakeFlashloan Action contract
 * @notice Executes a sequence of Actions after flashloaning funds
 */
contract TakeFlashloan is Executable, ProxyPermission {
  ServiceRegistry internal immutable registry;
  address internal immutable dai;

  constructor(ServiceRegistry _registry, address _dai) {
    registry = _registry;
    dai = _dai;
  }

  /**
   * @dev When the Flashloan lender calls back the Operation Executor we may need to re-establish the calling context.
   * @dev The dsProxyFlashloan flag is used to give the Operation Executor temporary authority to call the execute method on a user's proxy
   * @param data Encoded calldata that conforms to the FlashloanData struct
   */
  function execute(bytes calldata data, uint8[] memory) external payable override {
    FlashloanData memory flData = parseInputs(data);

    address operationExecutorAddress = registry.getRegisteredService(OPERATION_EXECUTOR);

    if (flData.dsProxyFlashloan) {
      givePermission(operationExecutorAddress);
    }

    IERC3156FlashLender(registry.getRegisteredService(FLASH_MINT_MODULE)).flashLoan(
      IERC3156FlashBorrower(operationExecutorAddress),
      dai,
      flData.amount,
      data
    );

    if (flData.dsProxyFlashloan) {
      removePermission(operationExecutorAddress);
    }

    emit Action(TAKE_FLASH_LOAN_ACTION, bytes32(flData.amount));
  }

  function parseInputs(bytes memory _callData) public pure returns (FlashloanData memory params) {
    return abi.decode(_callData, (FlashloanData));
  }
}

File 8 of 22 : Executable.sol
pragma solidity ^0.8.15;

/**
 * @title Shared Action Executable interface
 * @notice Provides a common interface for an execute method to all Action
 */
interface Executable {
  function execute(bytes calldata data, uint8[] memory paramsMap) external payable;

  /**
   * @dev Emitted once an Action has completed execution
   * @param name The Action name
   * @param returned The bytes32 value returned by the Action
   **/
  event Action(string name, bytes32 returned);
}

File 9 of 22 : IERC3156FlashBorrower.sol
// SPDX-License-Identifier: AGPL-3.0-or-later
// Copyright (C) 2021 Dai Foundation
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

pragma solidity ^0.8.15;

interface IERC3156FlashBorrower {
  /**
   * @dev Receive a flash loan.
   * @param initiator The initiator of the loan.
   * @param token The loan currency.
   * @param amount The amount of tokens lent.
   * @param fee The additional amount of tokens to repay.
   * @param data Arbitrary data structure, intended to contain user-defined parameters.
   * @return The keccak256 hash of "ERC3156FlashBorrower.onFlashLoan"
   */
  function onFlashLoan(
    address initiator,
    address token,
    uint256 amount,
    uint256 fee,
    bytes calldata data
  ) external returns (bytes32);
}

File 10 of 22 : IERC3156FlashLender.sol
// SPDX-License-Identifier: AGPL-3.0-or-later
// Copyright (C) 2021 Dai Foundation
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

pragma solidity ^0.8.15;

import "./IERC3156FlashBorrower.sol";

interface IERC3156FlashLender {
  /**
   * @dev The amount of currency available to be lent.
   * @param token The loan currency.
   * @return The amount of `token` that can be borrowed.
   */
  function maxFlashLoan(address token) external view returns (uint256);

  /**
   * @dev The fee to be charged for a given loan.
   * @param token The loan currency.
   * @param amount The amount of tokens lent.
   * @return The amount of `token` to be charged for the loan, on top of the returned principal.
   */
  function flashFee(address token, uint256 amount) external view returns (uint256);

  /**
   * @dev Initiate a flash loan.
   * @param receiver The receiver of the tokens in the loan, and the receiver of the callback.
   * @param token The loan currency.
   * @param amount The amount of tokens lent.
   * @param data Arbitrary data structure, intended to contain user-defined parameters.
   */
  function flashLoan(
    IERC3156FlashBorrower receiver,
    address token,
    uint256 amount,
    bytes calldata data
  ) external returns (bool);
}

File 11 of 22 : SafeERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.8.1;

import { IERC20 } from "../interfaces/tokens/IERC20.sol";
import { Address } from "./Address.sol";
import { SafeMath } from "./SafeMath.sol";

library SafeERC20 {
  using SafeMath for uint256;
  using Address for address;

  function safeTransfer(
    IERC20 token,
    address to,
    uint256 value
  ) internal {
    _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
  }

  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
   * {ERC20-approve}, and its usage is discouraged.
   */
  function safeApprove(
    IERC20 token,
    address spender,
    uint256 value
  ) internal {
    _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
    _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
  }

  function safeIncreaseAllowance(
    IERC20 token,
    address spender,
    uint256 value
  ) internal {
    uint256 newAllowance = token.allowance(address(this), spender).add(value);
    _callOptionalReturn(
      token,
      abi.encodeWithSelector(token.approve.selector, spender, newAllowance)
    );
  }

  function safeDecreaseAllowance(
    IERC20 token,
    address spender,
    uint256 value
  ) internal {
    uint256 newAllowance = token.allowance(address(this), spender).sub(
      value,
      "SafeERC20: decreased allowance below zero"
    );
    _callOptionalReturn(
      token,
      abi.encodeWithSelector(token.approve.selector, spender, newAllowance)
    );
  }

  function _callOptionalReturn(IERC20 token, bytes memory data) private {
    bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
    if (returndata.length > 0) {
      // Return data is optional
      // solhint-disable-next-line max-line-length
      require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
    }
  }
}

File 12 of 22 : SafeMath.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.15;

library SafeMath {
  function add(uint256 a, uint256 b) internal pure returns (uint256) {
    uint256 c = a + b;
    require(c >= a, "SafeMath: addition overflow");

    return c;
  }

  function sub(uint256 a, uint256 b) internal pure returns (uint256) {
    return sub(a, b, "SafeMath: subtraction overflow");
  }

  function sub(
    uint256 a,
    uint256 b,
    string memory errorMessage
  ) internal pure returns (uint256) {
    require(b <= a, errorMessage);
    uint256 c = a - b;

    return c;
  }

  function mul(uint256 a, uint256 b) internal pure returns (uint256) {
    // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
    // benefit is lost if 'b' is also tested.
    // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
    if (a == 0) {
      return 0;
    }

    uint256 c = a * b;
    require(c / a == b, "SafeMath: multiplication overflow");

    return c;
  }

  function div(uint256 a, uint256 b) internal pure returns (uint256) {
    return div(a, b, "SafeMath: division by zero");
  }

  function div(
    uint256 a,
    uint256 b,
    string memory errorMessage
  ) internal pure returns (uint256) {
    require(b > 0, errorMessage);
    uint256 c = a / b;
    // assert(a == b * c + a % b); // There is no case in which this doesn't hold

    return c;
  }

  function mod(uint256 a, uint256 b) internal pure returns (uint256) {
    return mod(a, b, "SafeMath: modulo by zero");
  }

  function mod(
    uint256 a,
    uint256 b,
    string memory errorMessage
  ) internal pure returns (uint256) {
    require(b != 0, errorMessage);
    return a % b;
  }
}

File 13 of 22 : Common.sol
pragma solidity ^0.8.15;

struct FlashloanData {
  uint256 amount;
  bool dsProxyFlashloan;
  Call[] calls;
}

struct PullTokenData {
  address asset;
  address from;
  uint256 amount;
}

struct SendTokenData {
  address asset;
  address to;
  uint256 amount;
}

struct SetApprovalData {
  address asset;
  address delegate;
  uint256 amount;
}

struct SwapData {
  address fromAsset;
  address toAsset;
  uint256 amount;
  uint256 receiveAtLeast;
  uint256 fee;
  bytes withData;
  bool collectFeeInFromToken;
}

struct Call {
  bytes32 targetHash;
  bytes callData;
}

struct Operation {
  uint8 currentAction;
  bytes32[] actions;
}

struct WrapEthData {
  uint256 amount;
}

struct UnwrapEthData {
  uint256 amount;
}

struct ReturnFundsData {
  address asset;
}

File 14 of 22 : Common.sol
pragma solidity ^0.8.15;

string constant OPERATION_STORAGE = "OperationStorage";
string constant OPERATION_EXECUTOR = "OperationExecutor";
string constant OPERATIONS_REGISTRY = "OperationsRegistry";
string constant ONE_INCH_AGGREGATOR = "OneInchAggregator";
string constant WETH = "WETH";
string constant DAI = "DAI";
uint256 constant RAY = 10**27;
bytes32 constant NULL = "";

string constant PULL_TOKEN_ACTION = "PullToken";
string constant SEND_TOKEN_ACTION = "SendToken";
string constant SET_APPROVAL_ACTION = "SetApproval";
string constant TAKE_FLASH_LOAN_ACTION = "TakeFlashloan";
string constant WRAP_ETH = "WrapEth";
string constant UNWRAP_ETH = "UnwrapEth";
string constant RETURN_FUNDS_ACTION = "ReturnFunds";

string constant UNISWAP_ROUTER = "UniswapRouter";
string constant SWAP = "Swap";

address constant ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;

File 15 of 22 : Maker.sol
pragma solidity ^0.8.15;

string constant FLASH_MINT_MODULE = "McdFlashMintModule";
string constant OPEN_VAULT_ACTION = "MakerOpenVault";
string constant DEPOSIT_ACTION = "MakerDeposit";
string constant GENERATE_ACTION = "MakerGenerate";
string constant PAYBACK_ACTION = "MakerPayback";
string constant WITHDRAW_ACTION = "MakerWithdraw";
string constant MCD_MANAGER = "McdManager";
string constant MCD_JUG = "McdJug";
string constant MCD_JOIN_DAI = "McdJoinDai";
string constant CDP_ALLOW = "MakerCdpAllow";

File 16 of 22 : DSAuth.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.15;

import "./DSAuthority.sol";

contract DSAuthEvents {
  event LogSetAuthority(address indexed authority);
  event LogSetOwner(address indexed owner);
}

contract DSAuth is DSAuthEvents {
  DSAuthority public authority;
  address public owner;

  constructor() {
    owner = msg.sender;
    emit LogSetOwner(msg.sender);
  }

  function setOwner(address owner_) public auth {
    owner = owner_;
    emit LogSetOwner(owner);
  }

  function setAuthority(DSAuthority authority_) public auth {
    authority = authority_;
    emit LogSetAuthority(address(authority));
  }

  modifier auth() {
    require(isAuthorized(msg.sender, msg.sig), "Not authorized");
    _;
  }

  function isAuthorized(address src, bytes4 sig) internal view returns (bool) {
    if (src == address(this)) {
      return true;
    } else if (src == owner) {
      return true;
    } else if (authority == DSAuthority(address(0))) {
      return false;
    } else {
      return authority.canCall(src, address(this), sig);
    }
  }
}

File 17 of 22 : DSNote.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.15;

contract DSNote {
  event LogNote(
    bytes4 indexed sig,
    address indexed guy,
    bytes32 indexed foo,
    bytes32 indexed bar,
    uint256 wad,
    bytes fax
  ) anonymous;

  modifier note() {
    bytes32 foo;
    bytes32 bar;

    assembly {
      foo := calldataload(4)
      bar := calldataload(36)
    }

    emit LogNote(msg.sig, msg.sender, foo, bar, msg.value, msg.data);

    _;
  }
}

File 18 of 22 : DSAuthority.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.15;

abstract contract DSAuthority {
  function canCall(
    address src,
    address dst,
    bytes4 sig
  ) public view virtual returns (bool);
}

File 19 of 22 : Address.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.8.1;

library Address {
  function isContract(address account) internal view returns (bool) {
    // According to EIP-1052, 0x0 is the value returned for not-yet created accounts
    // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
    // for accounts without code, i.e. `keccak256('')`
    bytes32 codehash;
    bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
    // solhint-disable-next-line no-inline-assembly
    assembly {
      codehash := extcodehash(account)
    }
    return (codehash != accountHash && codehash != 0x0);
  }

  function sendValue(address payable recipient, uint256 amount) internal {
    require(address(this).balance >= amount, "Address: insufficient balance");

    // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
    (bool success, ) = recipient.call{ value: amount }("");
    require(success, "Address: unable to send value, recipient may have reverted");
  }

  function functionCall(address target, bytes memory data) internal returns (bytes memory) {
    return functionCall(target, data, "Address: low-level call failed");
  }

  function functionCall(
    address target,
    bytes memory data,
    string memory errorMessage
  ) internal returns (bytes memory) {
    return _functionCallWithValue(target, data, 0, errorMessage);
  }

  function functionCallWithValue(
    address target,
    bytes memory data,
    uint256 value
  ) internal returns (bytes memory) {
    return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
  }

  function functionCallWithValue(
    address target,
    bytes memory data,
    uint256 value,
    string memory errorMessage
  ) internal returns (bytes memory) {
    require(address(this).balance >= value, "Address: insufficient balance for call");
    return _functionCallWithValue(target, data, value, errorMessage);
  }

  function _functionCallWithValue(
    address target,
    bytes memory data,
    uint256 weiValue,
    string memory errorMessage
  ) private returns (bytes memory) {
    require(isContract(target), "Address: call to non-contract");

    (bool success, bytes memory returndata) = target.call{ value: weiValue }(data);
    if (success) {
      return returndata;
    } else {
      // Look for revert reason and bubble it up if present
      if (returndata.length > 0) {
        // The easiest way to bubble the revert reason is using memory via assembly

        // solhint-disable-next-line no-inline-assembly
        assembly {
          let returndata_size := mload(returndata)
          revert(add(32, returndata), returndata_size)
        }
      } else {
        revert(errorMessage);
      }
    }
  }

  function functionDelegateCall(
    address target,
    bytes memory data,
    string memory errorMessage
  ) internal returns (bytes memory) {
    require(isContract(target), "Address: delegate call to non-contract");

    (bool success, bytes memory returndata) = target.delegatecall(data);
    if (success) {
      return returndata;
    }

    if (returndata.length > 0) {
      assembly {
        let returndata_size := mload(returndata)
        revert(add(32, returndata), returndata_size)
      }
    }

    revert(errorMessage);
  }
}

File 20 of 22 : ProxyPermission.sol
//SPDX-License-Identifier: Unlicense

pragma solidity ^0.8.15;

import "./DSGuard.sol";
import "./DSAuth.sol";

contract ProxyPermission {
  address internal constant FACTORY_ADDRESS = 0x5a15566417e6C1c9546523066500bDDBc53F88C7;

  bytes4 public constant ALLOWED_METHOD_HASH = bytes4(keccak256("execute(address,bytes)"));

  function givePermission(address _contractAddr) public {
    address currAuthority = address(DSAuth(address(this)).authority());
    DSGuard guard = DSGuard(currAuthority);

    if (currAuthority == address(0)) {
      guard = DSGuardFactory(FACTORY_ADDRESS).newGuard();
      DSAuth(address(this)).setAuthority(DSAuthority(address(guard)));
    }

    if (!guard.canCall(_contractAddr, address(this), ALLOWED_METHOD_HASH)) {
      guard.permit(_contractAddr, address(this), ALLOWED_METHOD_HASH);
    }
  }

  function removePermission(address _contractAddr) public {
    address currAuthority = address(DSAuth(address(this)).authority());

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

    DSGuard guard = DSGuard(currAuthority);
    guard.forbid(_contractAddr, address(this), ALLOWED_METHOD_HASH);
  }
}

File 21 of 22 : DSGuard.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.15;

abstract contract DSGuard {
  function canCall(
    address src_,
    address dst_,
    bytes4 sig
  ) public view virtual returns (bool);

  function permit(
    bytes32 src,
    bytes32 dst,
    bytes32 sig
  ) public virtual;

  function forbid(
    bytes32 src,
    bytes32 dst,
    bytes32 sig
  ) public virtual;

  function permit(
    address src,
    address dst,
    bytes32 sig
  ) public virtual;

  function forbid(
    address src,
    address dst,
    bytes32 sig
  ) public virtual;
}

abstract contract DSGuardFactory {
  function newGuard() public virtual returns (DSGuard guard);
}

File 22 of 22 : IERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.15;

interface IERC20 {
  function totalSupply() external view returns (uint256 supply);

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

  function transfer(address _to, uint256 _value) external returns (bool success);

  function transferFrom(
    address _from,
    address _to,
    uint256 _value
  ) external returns (bool success);

  function approve(address _spender, uint256 _value) external returns (bool success);

  function allowance(address _owner, address _spender) external view returns (uint256 remaining);

  function decimals() external view returns (uint256 digits);
}

Settings
{
  "optimizer": {
    "enabled": false,
    "runs": 200
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"contract ServiceRegistry","name":"_registry","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"name","type":"string"},{"components":[{"internalType":"bytes32","name":"targetHash","type":"bytes32"},{"internalType":"bytes","name":"callData","type":"bytes"}],"indexed":false,"internalType":"struct Call[]","name":"calls","type":"tuple[]"}],"name":"Operation","type":"event"},{"inputs":[{"components":[{"internalType":"bytes32","name":"targetHash","type":"bytes32"},{"internalType":"bytes","name":"callData","type":"bytes"}],"internalType":"struct Call[]","name":"calls","type":"tuple[]"}],"name":"callbackAggregate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32","name":"targetHash","type":"bytes32"},{"internalType":"bytes","name":"callData","type":"bytes"}],"internalType":"struct Call[]","name":"calls","type":"tuple[]"},{"internalType":"string","name":"operationName","type":"string"}],"name":"executeOp","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"initiator","type":"address"},{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"fee","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"onFlashLoan","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"registry","outputs":[{"internalType":"contract ServiceRegistry","name":"","type":"address"}],"stateMutability":"view","type":"function"}]

60a06040523480156200001157600080fd5b5060405162002baa38038062002baa8339818101604052810190620000379190620000f0565b8073ffffffffffffffffffffffffffffffffffffffff1660808173ffffffffffffffffffffffffffffffffffffffff16815250505062000122565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000620000a48262000077565b9050919050565b6000620000b88262000097565b9050919050565b620000ca81620000ab565b8114620000d657600080fd5b50565b600081519050620000ea81620000bf565b92915050565b60006020828403121562000109576200010862000072565b5b60006200011984828501620000d9565b91505092915050565b608051612a3c6200016e6000396000818160f601528181610440015281816106b901528181610801015281816108270152818161095a01528181610d090152610eeb0152612a3c6000f3fe60806040526004361061003f5760003560e01c806323e30c8b146100445780634fa0b9ff146100815780637b103999146100aa578063c4a8b32f146100d5575b600080fd5b34801561005057600080fd5b5061006b60048036038101906100669190611763565b6100f1565b6040516100789190611816565b60405180910390f35b34801561008d57600080fd5b506100a860048036038101906100a39190611af5565b6106b7565b005b3480156100b657600080fd5b506100bf6107ff565b6040516100cc9190611b9d565b60405180910390f35b6100ef60048036038101906100ea9190611c0e565b610823565b005b6000807f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16630851f3bd6040518060400160405280601281526020017f4d6364466c6173684d696e744d6f64756c6500000000000000000000000000008152506040518263ffffffff1660e01b81526004016101829190611d12565b602060405180830381865afa15801561019f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101c39190611d49565b90508073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610233576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161022a90611dc2565b60405180910390fd5b600084848101906102449190611e9a565b905080600001518873ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b81526004016102849190611ef2565b602060405180830381865afa1580156102a1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102c59190611f22565b1015610306576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016102fd90611f9b565b60405180910390fd5b80602001511561043c5761033f8982600001518a73ffffffffffffffffffffffffffffffffffffffff16610c7f9092919063ffffffff16565b8873ffffffffffffffffffffffffffffffffffffffff16631cff79cd30634fa0b9ff60e01b8460400151604051602401610379919061211e565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040518363ffffffff1660e01b81526004016103f392919061218a565b6020604051808303816000875af1158015610412573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061043691906121cf565b50610589565b60007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16630851f3bd6040518060400160405280601081526020017f4f7065726174696f6e53746f72616765000000000000000000000000000000008152506040518263ffffffff1660e01b81526004016104cc9190611d12565b602060405180830381865afa1580156104e9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061050d9190611d49565b90508073ffffffffffffffffffffffffffffffffffffffff1663d59dfd618b6040518263ffffffff1660e01b81526004016105489190611ef2565b600060405180830381600087803b15801561056257600080fd5b505af1158015610576573d6000803e3d6000fd5b505050506105878260400151610d05565b505b600061059e878961100490919063ffffffff16565b9050808973ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b81526004016105da9190611ef2565b602060405180830381865afa1580156105f7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061061b9190611f22565b101561065c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161065390612248565b60405180910390fd5b61068783828b73ffffffffffffffffffffffffffffffffffffffff166110629092919063ffffffff16565b7f439148f0bbc682ca079e46d6e2c2f0c1e3b820f1a291b069d8882abf8cf18dd993505050509695505050505050565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16630851f3bd6040518060400160405280601181526020017f4f7065726174696f6e4578656375746f720000000000000000000000000000008152506040518263ffffffff1660e01b81526004016107459190611d12565b602060405180830381865afa158015610762573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107869190611d49565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146107f3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107ea906122b4565b60405180910390fd5b6107fc81610d05565b50565b7f000000000000000000000000000000000000000000000000000000000000000081565b60007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16630851f3bd6040518060400160405280601081526020017f4f7065726174696f6e53746f72616765000000000000000000000000000000008152506040518263ffffffff1660e01b81526004016108b39190611d12565b602060405180830381865afa1580156108d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108f49190611d49565b90508073ffffffffffffffffffffffffffffffffffffffff1663f83d08ba6040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561093e57600080fd5b505af1158015610952573d6000803e3d6000fd5b5050505060007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16630851f3bd6040518060400160405280601281526020017f4f7065726174696f6e73526567697374727900000000000000000000000000008152506040518263ffffffff1660e01b81526004016109e69190611d12565b602060405180830381865afa158015610a03573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a279190611d49565b90508173ffffffffffffffffffffffffffffffffffffffff166369bd38a06040518163ffffffff1660e01b8152600401600060405180830381600087803b158015610a7157600080fd5b505af1158015610a85573d6000803e3d6000fd5b505050508173ffffffffffffffffffffffffffffffffffffffff1663b082cbfc8273ffffffffffffffffffffffffffffffffffffffff16631fffb05c87876040518363ffffffff1660e01b8152600401610ae0929190612301565b600060405180830381865afa158015610afd573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f82011682018060405250810190610b2691906123e8565b6040518263ffffffff1660e01b8152600401610b4291906124e0565b600060405180830381600087803b158015610b5c57600080fd5b505af1158015610b70573d6000803e3d6000fd5b50505050610b7d85610d05565b8173ffffffffffffffffffffffffffffffffffffffff166369bd38a06040518163ffffffff1660e01b8152600401600060405180830381600087803b158015610bc557600080fd5b505af1158015610bd9573d6000803e3d6000fd5b505050508173ffffffffffffffffffffffffffffffffffffffff1663a69df4b56040518163ffffffff1660e01b8152600401600060405180830381600087803b158015610c2557600080fd5b505af1158015610c39573d6000803e3d6000fd5b505050507fcc558ab90a763767f0ca2c8bd53b5dcad35d2a3da3e5bd6e585b48ae8f4210ab848487604051610c7093929190612502565b60405180910390a15050505050565b610d008363a9059cbb60e01b8484604051602401610c9e92919061254a565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505061116a565b505050565b60007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16630851f3bd6040518060400160405280601081526020017f4f7065726174696f6e53746f72616765000000000000000000000000000000008152506040518263ffffffff1660e01b8152600401610d959190611d12565b602060405180830381865afa158015610db2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dd69190611d49565b905060008173ffffffffffffffffffffffffffffffffffffffff16634b1824a46040518163ffffffff1660e01b8152600401602060405180830381865afa158015610e25573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e499190612588565b905060005b8351811015610ffe578115610ee7578273ffffffffffffffffffffffffffffffffffffffff16638abe863c858381518110610e8c57610e8b6125b5565b5b6020026020010151600001516040518263ffffffff1660e01b8152600401610eb49190611816565b600060405180830381600087803b158015610ece57600080fd5b505af1158015610ee2573d6000803e3d6000fd5b505050505b60007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663c2527b32868481518110610f3857610f376125b5565b5b6020026020010151600001516040518263ffffffff1660e01b8152600401610f609190611816565b602060405180830381865afa158015610f7d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fa19190611d49565b9050610fea858381518110610fb957610fb86125b5565b5b6020026020010151602001518273ffffffffffffffffffffffffffffffffffffffff1661123190919063ffffffff16565b508080610ff690612613565b915050610e4e565b50505050565b6000808284611013919061265b565b905083811015611058576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161104f906126fd565b60405180910390fd5b8091505092915050565b6110e48363095ea7b360e01b846000604051602401611082929190612765565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505061116a565b6111658363095ea7b360e01b848460405160240161110392919061254a565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505061116a565b505050565b60006111cc826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648152508573ffffffffffffffffffffffffffffffffffffffff166112c19092919063ffffffff16565b905060008151111561122c57808060200190518101906111ec9190612588565b61122b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161122290612800565b60405180910390fd5b5b505050565b61123a816112d9565b611279576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016112709061286c565b60405180910390fd5b6112bc816040518060600160405280602981526020016129de602991398473ffffffffffffffffffffffffffffffffffffffff166113a89092919063ffffffff16565b505050565b60606112d084846000856114c6565b90509392505050565b60008061134f6385e92d9860e01b604051602401604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506115e8565b9050600061135c846115e8565b9050817bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916817bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19161492505050919050565b60606113b38461160b565b6113f2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016113e9906128fe565b60405180910390fd5b6000808573ffffffffffffffffffffffffffffffffffffffff168560405161141a919061295a565b600060405180830381855af49150503d8060008114611455576040519150601f19603f3d011682016040523d82523d6000602084013e61145a565b606091505b5091509150811561146f5780925050506114bf565b6000815111156114825780518082602001fd5b836040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016114b69190611d12565b60405180910390fd5b9392505050565b60606114d18561160b565b611510576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611507906129bd565b60405180910390fd5b6000808673ffffffffffffffffffffffffffffffffffffffff168587604051611539919061295a565b60006040518083038185875af1925050503d8060008114611576576040519150601f19603f3d011682016040523d82523d6000602084013e61157b565b606091505b509150915081156115905780925050506115e0565b6000815111156115a35780518082602001fd5b836040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016115d79190611d12565b60405180910390fd5b949350505050565b6000808251036115fe57600060e01b9050611606565b602082015190505b919050565b60008060007fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47060001b9050833f915080821415801561164d57506000801b8214155b92505050919050565b6000604051905090565b600080fd5b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006116958261166a565b9050919050565b6116a58161168a565b81146116b057600080fd5b50565b6000813590506116c28161169c565b92915050565b6000819050919050565b6116db816116c8565b81146116e657600080fd5b50565b6000813590506116f8816116d2565b92915050565b600080fd5b600080fd5b600080fd5b60008083601f840112611723576117226116fe565b5b8235905067ffffffffffffffff8111156117405761173f611703565b5b60208301915083600182028301111561175c5761175b611708565b5b9250929050565b60008060008060008060a087890312156117805761177f611660565b5b600061178e89828a016116b3565b965050602061179f89828a016116b3565b95505060406117b089828a016116e9565b94505060606117c189828a016116e9565b935050608087013567ffffffffffffffff8111156117e2576117e1611665565b5b6117ee89828a0161170d565b92509250509295509295509295565b6000819050919050565b611810816117fd565b82525050565b600060208201905061182b6000830184611807565b92915050565b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b61187a82611831565b810181811067ffffffffffffffff8211171561189957611898611842565b5b80604052505050565b60006118ac611656565b90506118b88282611871565b919050565b600067ffffffffffffffff8211156118d8576118d7611842565b5b602082029050602081019050919050565b600080fd5b600080fd5b6118fc816117fd565b811461190757600080fd5b50565b600081359050611919816118f3565b92915050565b600080fd5b600067ffffffffffffffff82111561193f5761193e611842565b5b61194882611831565b9050602081019050919050565b82818337600083830152505050565b600061197761197284611924565b6118a2565b9050828152602081018484840111156119935761199261191f565b5b61199e848285611955565b509392505050565b600082601f8301126119bb576119ba6116fe565b5b81356119cb848260208601611964565b91505092915050565b6000604082840312156119ea576119e96118e9565b5b6119f460406118a2565b90506000611a048482850161190a565b600083015250602082013567ffffffffffffffff811115611a2857611a276118ee565b5b611a34848285016119a6565b60208301525092915050565b6000611a53611a4e846118bd565b6118a2565b90508083825260208201905060208402830185811115611a7657611a75611708565b5b835b81811015611abd57803567ffffffffffffffff811115611a9b57611a9a6116fe565b5b808601611aa889826119d4565b85526020850194505050602081019050611a78565b5050509392505050565b600082601f830112611adc57611adb6116fe565b5b8135611aec848260208601611a40565b91505092915050565b600060208284031215611b0b57611b0a611660565b5b600082013567ffffffffffffffff811115611b2957611b28611665565b5b611b3584828501611ac7565b91505092915050565b6000819050919050565b6000611b63611b5e611b598461166a565b611b3e565b61166a565b9050919050565b6000611b7582611b48565b9050919050565b6000611b8782611b6a565b9050919050565b611b9781611b7c565b82525050565b6000602082019050611bb26000830184611b8e565b92915050565b60008083601f840112611bce57611bcd6116fe565b5b8235905067ffffffffffffffff811115611beb57611bea611703565b5b602083019150836001820283011115611c0757611c06611708565b5b9250929050565b600080600060408486031215611c2757611c26611660565b5b600084013567ffffffffffffffff811115611c4557611c44611665565b5b611c5186828701611ac7565b935050602084013567ffffffffffffffff811115611c7257611c71611665565b5b611c7e86828701611bb8565b92509250509250925092565b600081519050919050565b600082825260208201905092915050565b60005b83811015611cc4578082015181840152602081019050611ca9565b83811115611cd3576000848401525b50505050565b6000611ce482611c8a565b611cee8185611c95565b9350611cfe818560208601611ca6565b611d0781611831565b840191505092915050565b60006020820190508181036000830152611d2c8184611cd9565b905092915050565b600081519050611d438161169c565b92915050565b600060208284031215611d5f57611d5e611660565b5b6000611d6d84828501611d34565b91505092915050565b7f556e7472757374656420666c6173686c6f616e206c656e646572000000000000600082015250565b6000611dac601a83611c95565b9150611db782611d76565b602082019050919050565b60006020820190508181036000830152611ddb81611d9f565b9050919050565b60008115159050919050565b611df781611de2565b8114611e0257600080fd5b50565b600081359050611e1481611dee565b92915050565b600060608284031215611e3057611e2f6118e9565b5b611e3a60606118a2565b90506000611e4a848285016116e9565b6000830152506020611e5e84828501611e05565b602083015250604082013567ffffffffffffffff811115611e8257611e816118ee565b5b611e8e84828501611ac7565b60408301525092915050565b600060208284031215611eb057611eaf611660565b5b600082013567ffffffffffffffff811115611ece57611ecd611665565b5b611eda84828501611e1a565b91505092915050565b611eec8161168a565b82525050565b6000602082019050611f076000830184611ee3565b92915050565b600081519050611f1c816116d2565b92915050565b600060208284031215611f3857611f37611660565b5b6000611f4684828501611f0d565b91505092915050565b7f466c6173686c6f616e20696e636f6e73697374656e6379000000000000000000600082015250565b6000611f85601783611c95565b9150611f9082611f4f565b602082019050919050565b60006020820190508181036000830152611fb481611f78565b9050919050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b611ff0816117fd565b82525050565b600081519050919050565b600082825260208201905092915050565b600061201d82611ff6565b6120278185612001565b9350612037818560208601611ca6565b61204081611831565b840191505092915050565b60006040830160008301516120636000860182611fe7565b506020830151848203602086015261207b8282612012565b9150508091505092915050565b6000612094838361204b565b905092915050565b6000602082019050919050565b60006120b482611fbb565b6120be8185611fc6565b9350836020820285016120d085611fd7565b8060005b8581101561210c57848403895281516120ed8582612088565b94506120f88361209c565b925060208a019950506001810190506120d4565b50829750879550505050505092915050565b6000602082019050818103600083015261213881846120a9565b905092915050565b600082825260208201905092915050565b600061215c82611ff6565b6121668185612140565b9350612176818560208601611ca6565b61217f81611831565b840191505092915050565b600060408201905061219f6000830185611ee3565b81810360208301526121b18184612151565b90509392505050565b6000815190506121c9816118f3565b92915050565b6000602082840312156121e5576121e4611660565b5b60006121f3848285016121ba565b91505092915050565b7f496e73756666696369656e742066756e647320666f72207061796261636b0000600082015250565b6000612232601e83611c95565b915061223d826121fc565b602082019050919050565b6000602082019050818103600083015261226181612225565b9050919050565b7f4f704578656375746f723a2043616c6c657220666f7262696464656e00000000600082015250565b600061229e601c83611c95565b91506122a982612268565b602082019050919050565b600060208201905081810360008301526122cd81612291565b9050919050565b60006122e08385611c95565b93506122ed838584611955565b6122f683611831565b840190509392505050565b6000602082019050818103600083015261231c8184866122d4565b90509392505050565b600067ffffffffffffffff8211156123405761233f611842565b5b602082029050602081019050919050565b600061236461235f84612325565b6118a2565b9050808382526020820190506020840283018581111561238757612386611708565b5b835b818110156123b0578061239c88826121ba565b845260208401935050602081019050612389565b5050509392505050565b600082601f8301126123cf576123ce6116fe565b5b81516123df848260208601612351565b91505092915050565b6000602082840312156123fe576123fd611660565b5b600082015167ffffffffffffffff81111561241c5761241b611665565b5b612428848285016123ba565b91505092915050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b60006124698383611fe7565b60208301905092915050565b6000602082019050919050565b600061248d82612431565b612497818561243c565b93506124a28361244d565b8060005b838110156124d35781516124ba888261245d565b97506124c583612475565b9250506001810190506124a6565b5085935050505092915050565b600060208201905081810360008301526124fa8184612482565b905092915050565b6000604082019050818103600083015261251d8185876122d4565b9050818103602083015261253181846120a9565b9050949350505050565b612544816116c8565b82525050565b600060408201905061255f6000830185611ee3565b61256c602083018461253b565b9392505050565b60008151905061258281611dee565b92915050565b60006020828403121561259e5761259d611660565b5b60006125ac84828501612573565b91505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600061261e826116c8565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036126505761264f6125e4565b5b600182019050919050565b6000612666826116c8565b9150612671836116c8565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff038211156126a6576126a56125e4565b5b828201905092915050565b7f536166654d6174683a206164646974696f6e206f766572666c6f770000000000600082015250565b60006126e7601b83611c95565b91506126f2826126b1565b602082019050919050565b60006020820190508181036000830152612716816126da565b9050919050565b6000819050919050565b600060ff82169050919050565b600061274f61274a6127458461271d565b611b3e565b612727565b9050919050565b61275f81612734565b82525050565b600060408201905061277a6000830185611ee3565b6127876020830184612756565b9392505050565b7f5361666545524332303a204552433230206f7065726174696f6e20646964206e60008201527f6f74207375636365656400000000000000000000000000000000000000000000602082015250565b60006127ea602a83611c95565b91506127f58261278e565b604082019050919050565b60006020820190508181036000830152612819816127dd565b9050919050565b7f4f704578656375746f723a20696c6c6567616c2063616c6c0000000000000000600082015250565b6000612856601883611c95565b915061286182612820565b602082019050919050565b6000602082019050818103600083015261288581612849565b9050919050565b7f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f60008201527f6e74726163740000000000000000000000000000000000000000000000000000602082015250565b60006128e8602683611c95565b91506128f38261288c565b604082019050919050565b60006020820190508181036000830152612917816128db565b9050919050565b600081905092915050565b600061293482611ff6565b61293e818561291e565b935061294e818560208601611ca6565b80840191505092915050565b60006129668284612929565b915081905092915050565b7f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000600082015250565b60006129a7601d83611c95565b91506129b282612971565b602082019050919050565b600060208201905081810360008301526129d68161299a565b905091905056fe4f704578656375746f723a206c6f772d6c6576656c2064656c656761746563616c6c206661696c6564a2646970667358221220ed9d43a5b4a9bd69ea5ac17fb51616fa6fb15f22ca68403ac5d00789beb9090064736f6c634300080f00330000000000000000000000009b4ae7b164d195df9c4da5d08be88b2848b2eada

Deployed Bytecode

0x60806040526004361061003f5760003560e01c806323e30c8b146100445780634fa0b9ff146100815780637b103999146100aa578063c4a8b32f146100d5575b600080fd5b34801561005057600080fd5b5061006b60048036038101906100669190611763565b6100f1565b6040516100789190611816565b60405180910390f35b34801561008d57600080fd5b506100a860048036038101906100a39190611af5565b6106b7565b005b3480156100b657600080fd5b506100bf6107ff565b6040516100cc9190611b9d565b60405180910390f35b6100ef60048036038101906100ea9190611c0e565b610823565b005b6000807f0000000000000000000000009b4ae7b164d195df9c4da5d08be88b2848b2eada73ffffffffffffffffffffffffffffffffffffffff16630851f3bd6040518060400160405280601281526020017f4d6364466c6173684d696e744d6f64756c6500000000000000000000000000008152506040518263ffffffff1660e01b81526004016101829190611d12565b602060405180830381865afa15801561019f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101c39190611d49565b90508073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610233576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161022a90611dc2565b60405180910390fd5b600084848101906102449190611e9a565b905080600001518873ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b81526004016102849190611ef2565b602060405180830381865afa1580156102a1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102c59190611f22565b1015610306576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016102fd90611f9b565b60405180910390fd5b80602001511561043c5761033f8982600001518a73ffffffffffffffffffffffffffffffffffffffff16610c7f9092919063ffffffff16565b8873ffffffffffffffffffffffffffffffffffffffff16631cff79cd30634fa0b9ff60e01b8460400151604051602401610379919061211e565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040518363ffffffff1660e01b81526004016103f392919061218a565b6020604051808303816000875af1158015610412573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061043691906121cf565b50610589565b60007f0000000000000000000000009b4ae7b164d195df9c4da5d08be88b2848b2eada73ffffffffffffffffffffffffffffffffffffffff16630851f3bd6040518060400160405280601081526020017f4f7065726174696f6e53746f72616765000000000000000000000000000000008152506040518263ffffffff1660e01b81526004016104cc9190611d12565b602060405180830381865afa1580156104e9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061050d9190611d49565b90508073ffffffffffffffffffffffffffffffffffffffff1663d59dfd618b6040518263ffffffff1660e01b81526004016105489190611ef2565b600060405180830381600087803b15801561056257600080fd5b505af1158015610576573d6000803e3d6000fd5b505050506105878260400151610d05565b505b600061059e878961100490919063ffffffff16565b9050808973ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b81526004016105da9190611ef2565b602060405180830381865afa1580156105f7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061061b9190611f22565b101561065c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161065390612248565b60405180910390fd5b61068783828b73ffffffffffffffffffffffffffffffffffffffff166110629092919063ffffffff16565b7f439148f0bbc682ca079e46d6e2c2f0c1e3b820f1a291b069d8882abf8cf18dd993505050509695505050505050565b7f0000000000000000000000009b4ae7b164d195df9c4da5d08be88b2848b2eada73ffffffffffffffffffffffffffffffffffffffff16630851f3bd6040518060400160405280601181526020017f4f7065726174696f6e4578656375746f720000000000000000000000000000008152506040518263ffffffff1660e01b81526004016107459190611d12565b602060405180830381865afa158015610762573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107869190611d49565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146107f3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107ea906122b4565b60405180910390fd5b6107fc81610d05565b50565b7f0000000000000000000000009b4ae7b164d195df9c4da5d08be88b2848b2eada81565b60007f0000000000000000000000009b4ae7b164d195df9c4da5d08be88b2848b2eada73ffffffffffffffffffffffffffffffffffffffff16630851f3bd6040518060400160405280601081526020017f4f7065726174696f6e53746f72616765000000000000000000000000000000008152506040518263ffffffff1660e01b81526004016108b39190611d12565b602060405180830381865afa1580156108d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108f49190611d49565b90508073ffffffffffffffffffffffffffffffffffffffff1663f83d08ba6040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561093e57600080fd5b505af1158015610952573d6000803e3d6000fd5b5050505060007f0000000000000000000000009b4ae7b164d195df9c4da5d08be88b2848b2eada73ffffffffffffffffffffffffffffffffffffffff16630851f3bd6040518060400160405280601281526020017f4f7065726174696f6e73526567697374727900000000000000000000000000008152506040518263ffffffff1660e01b81526004016109e69190611d12565b602060405180830381865afa158015610a03573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a279190611d49565b90508173ffffffffffffffffffffffffffffffffffffffff166369bd38a06040518163ffffffff1660e01b8152600401600060405180830381600087803b158015610a7157600080fd5b505af1158015610a85573d6000803e3d6000fd5b505050508173ffffffffffffffffffffffffffffffffffffffff1663b082cbfc8273ffffffffffffffffffffffffffffffffffffffff16631fffb05c87876040518363ffffffff1660e01b8152600401610ae0929190612301565b600060405180830381865afa158015610afd573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f82011682018060405250810190610b2691906123e8565b6040518263ffffffff1660e01b8152600401610b4291906124e0565b600060405180830381600087803b158015610b5c57600080fd5b505af1158015610b70573d6000803e3d6000fd5b50505050610b7d85610d05565b8173ffffffffffffffffffffffffffffffffffffffff166369bd38a06040518163ffffffff1660e01b8152600401600060405180830381600087803b158015610bc557600080fd5b505af1158015610bd9573d6000803e3d6000fd5b505050508173ffffffffffffffffffffffffffffffffffffffff1663a69df4b56040518163ffffffff1660e01b8152600401600060405180830381600087803b158015610c2557600080fd5b505af1158015610c39573d6000803e3d6000fd5b505050507fcc558ab90a763767f0ca2c8bd53b5dcad35d2a3da3e5bd6e585b48ae8f4210ab848487604051610c7093929190612502565b60405180910390a15050505050565b610d008363a9059cbb60e01b8484604051602401610c9e92919061254a565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505061116a565b505050565b60007f0000000000000000000000009b4ae7b164d195df9c4da5d08be88b2848b2eada73ffffffffffffffffffffffffffffffffffffffff16630851f3bd6040518060400160405280601081526020017f4f7065726174696f6e53746f72616765000000000000000000000000000000008152506040518263ffffffff1660e01b8152600401610d959190611d12565b602060405180830381865afa158015610db2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dd69190611d49565b905060008173ffffffffffffffffffffffffffffffffffffffff16634b1824a46040518163ffffffff1660e01b8152600401602060405180830381865afa158015610e25573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e499190612588565b905060005b8351811015610ffe578115610ee7578273ffffffffffffffffffffffffffffffffffffffff16638abe863c858381518110610e8c57610e8b6125b5565b5b6020026020010151600001516040518263ffffffff1660e01b8152600401610eb49190611816565b600060405180830381600087803b158015610ece57600080fd5b505af1158015610ee2573d6000803e3d6000fd5b505050505b60007f0000000000000000000000009b4ae7b164d195df9c4da5d08be88b2848b2eada73ffffffffffffffffffffffffffffffffffffffff1663c2527b32868481518110610f3857610f376125b5565b5b6020026020010151600001516040518263ffffffff1660e01b8152600401610f609190611816565b602060405180830381865afa158015610f7d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fa19190611d49565b9050610fea858381518110610fb957610fb86125b5565b5b6020026020010151602001518273ffffffffffffffffffffffffffffffffffffffff1661123190919063ffffffff16565b508080610ff690612613565b915050610e4e565b50505050565b6000808284611013919061265b565b905083811015611058576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161104f906126fd565b60405180910390fd5b8091505092915050565b6110e48363095ea7b360e01b846000604051602401611082929190612765565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505061116a565b6111658363095ea7b360e01b848460405160240161110392919061254a565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505061116a565b505050565b60006111cc826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648152508573ffffffffffffffffffffffffffffffffffffffff166112c19092919063ffffffff16565b905060008151111561122c57808060200190518101906111ec9190612588565b61122b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161122290612800565b60405180910390fd5b5b505050565b61123a816112d9565b611279576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016112709061286c565b60405180910390fd5b6112bc816040518060600160405280602981526020016129de602991398473ffffffffffffffffffffffffffffffffffffffff166113a89092919063ffffffff16565b505050565b60606112d084846000856114c6565b90509392505050565b60008061134f6385e92d9860e01b604051602401604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506115e8565b9050600061135c846115e8565b9050817bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916817bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19161492505050919050565b60606113b38461160b565b6113f2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016113e9906128fe565b60405180910390fd5b6000808573ffffffffffffffffffffffffffffffffffffffff168560405161141a919061295a565b600060405180830381855af49150503d8060008114611455576040519150601f19603f3d011682016040523d82523d6000602084013e61145a565b606091505b5091509150811561146f5780925050506114bf565b6000815111156114825780518082602001fd5b836040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016114b69190611d12565b60405180910390fd5b9392505050565b60606114d18561160b565b611510576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611507906129bd565b60405180910390fd5b6000808673ffffffffffffffffffffffffffffffffffffffff168587604051611539919061295a565b60006040518083038185875af1925050503d8060008114611576576040519150601f19603f3d011682016040523d82523d6000602084013e61157b565b606091505b509150915081156115905780925050506115e0565b6000815111156115a35780518082602001fd5b836040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016115d79190611d12565b60405180910390fd5b949350505050565b6000808251036115fe57600060e01b9050611606565b602082015190505b919050565b60008060007fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47060001b9050833f915080821415801561164d57506000801b8214155b92505050919050565b6000604051905090565b600080fd5b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006116958261166a565b9050919050565b6116a58161168a565b81146116b057600080fd5b50565b6000813590506116c28161169c565b92915050565b6000819050919050565b6116db816116c8565b81146116e657600080fd5b50565b6000813590506116f8816116d2565b92915050565b600080fd5b600080fd5b600080fd5b60008083601f840112611723576117226116fe565b5b8235905067ffffffffffffffff8111156117405761173f611703565b5b60208301915083600182028301111561175c5761175b611708565b5b9250929050565b60008060008060008060a087890312156117805761177f611660565b5b600061178e89828a016116b3565b965050602061179f89828a016116b3565b95505060406117b089828a016116e9565b94505060606117c189828a016116e9565b935050608087013567ffffffffffffffff8111156117e2576117e1611665565b5b6117ee89828a0161170d565b92509250509295509295509295565b6000819050919050565b611810816117fd565b82525050565b600060208201905061182b6000830184611807565b92915050565b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b61187a82611831565b810181811067ffffffffffffffff8211171561189957611898611842565b5b80604052505050565b60006118ac611656565b90506118b88282611871565b919050565b600067ffffffffffffffff8211156118d8576118d7611842565b5b602082029050602081019050919050565b600080fd5b600080fd5b6118fc816117fd565b811461190757600080fd5b50565b600081359050611919816118f3565b92915050565b600080fd5b600067ffffffffffffffff82111561193f5761193e611842565b5b61194882611831565b9050602081019050919050565b82818337600083830152505050565b600061197761197284611924565b6118a2565b9050828152602081018484840111156119935761199261191f565b5b61199e848285611955565b509392505050565b600082601f8301126119bb576119ba6116fe565b5b81356119cb848260208601611964565b91505092915050565b6000604082840312156119ea576119e96118e9565b5b6119f460406118a2565b90506000611a048482850161190a565b600083015250602082013567ffffffffffffffff811115611a2857611a276118ee565b5b611a34848285016119a6565b60208301525092915050565b6000611a53611a4e846118bd565b6118a2565b90508083825260208201905060208402830185811115611a7657611a75611708565b5b835b81811015611abd57803567ffffffffffffffff811115611a9b57611a9a6116fe565b5b808601611aa889826119d4565b85526020850194505050602081019050611a78565b5050509392505050565b600082601f830112611adc57611adb6116fe565b5b8135611aec848260208601611a40565b91505092915050565b600060208284031215611b0b57611b0a611660565b5b600082013567ffffffffffffffff811115611b2957611b28611665565b5b611b3584828501611ac7565b91505092915050565b6000819050919050565b6000611b63611b5e611b598461166a565b611b3e565b61166a565b9050919050565b6000611b7582611b48565b9050919050565b6000611b8782611b6a565b9050919050565b611b9781611b7c565b82525050565b6000602082019050611bb26000830184611b8e565b92915050565b60008083601f840112611bce57611bcd6116fe565b5b8235905067ffffffffffffffff811115611beb57611bea611703565b5b602083019150836001820283011115611c0757611c06611708565b5b9250929050565b600080600060408486031215611c2757611c26611660565b5b600084013567ffffffffffffffff811115611c4557611c44611665565b5b611c5186828701611ac7565b935050602084013567ffffffffffffffff811115611c7257611c71611665565b5b611c7e86828701611bb8565b92509250509250925092565b600081519050919050565b600082825260208201905092915050565b60005b83811015611cc4578082015181840152602081019050611ca9565b83811115611cd3576000848401525b50505050565b6000611ce482611c8a565b611cee8185611c95565b9350611cfe818560208601611ca6565b611d0781611831565b840191505092915050565b60006020820190508181036000830152611d2c8184611cd9565b905092915050565b600081519050611d438161169c565b92915050565b600060208284031215611d5f57611d5e611660565b5b6000611d6d84828501611d34565b91505092915050565b7f556e7472757374656420666c6173686c6f616e206c656e646572000000000000600082015250565b6000611dac601a83611c95565b9150611db782611d76565b602082019050919050565b60006020820190508181036000830152611ddb81611d9f565b9050919050565b60008115159050919050565b611df781611de2565b8114611e0257600080fd5b50565b600081359050611e1481611dee565b92915050565b600060608284031215611e3057611e2f6118e9565b5b611e3a60606118a2565b90506000611e4a848285016116e9565b6000830152506020611e5e84828501611e05565b602083015250604082013567ffffffffffffffff811115611e8257611e816118ee565b5b611e8e84828501611ac7565b60408301525092915050565b600060208284031215611eb057611eaf611660565b5b600082013567ffffffffffffffff811115611ece57611ecd611665565b5b611eda84828501611e1a565b91505092915050565b611eec8161168a565b82525050565b6000602082019050611f076000830184611ee3565b92915050565b600081519050611f1c816116d2565b92915050565b600060208284031215611f3857611f37611660565b5b6000611f4684828501611f0d565b91505092915050565b7f466c6173686c6f616e20696e636f6e73697374656e6379000000000000000000600082015250565b6000611f85601783611c95565b9150611f9082611f4f565b602082019050919050565b60006020820190508181036000830152611fb481611f78565b9050919050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b611ff0816117fd565b82525050565b600081519050919050565b600082825260208201905092915050565b600061201d82611ff6565b6120278185612001565b9350612037818560208601611ca6565b61204081611831565b840191505092915050565b60006040830160008301516120636000860182611fe7565b506020830151848203602086015261207b8282612012565b9150508091505092915050565b6000612094838361204b565b905092915050565b6000602082019050919050565b60006120b482611fbb565b6120be8185611fc6565b9350836020820285016120d085611fd7565b8060005b8581101561210c57848403895281516120ed8582612088565b94506120f88361209c565b925060208a019950506001810190506120d4565b50829750879550505050505092915050565b6000602082019050818103600083015261213881846120a9565b905092915050565b600082825260208201905092915050565b600061215c82611ff6565b6121668185612140565b9350612176818560208601611ca6565b61217f81611831565b840191505092915050565b600060408201905061219f6000830185611ee3565b81810360208301526121b18184612151565b90509392505050565b6000815190506121c9816118f3565b92915050565b6000602082840312156121e5576121e4611660565b5b60006121f3848285016121ba565b91505092915050565b7f496e73756666696369656e742066756e647320666f72207061796261636b0000600082015250565b6000612232601e83611c95565b915061223d826121fc565b602082019050919050565b6000602082019050818103600083015261226181612225565b9050919050565b7f4f704578656375746f723a2043616c6c657220666f7262696464656e00000000600082015250565b600061229e601c83611c95565b91506122a982612268565b602082019050919050565b600060208201905081810360008301526122cd81612291565b9050919050565b60006122e08385611c95565b93506122ed838584611955565b6122f683611831565b840190509392505050565b6000602082019050818103600083015261231c8184866122d4565b90509392505050565b600067ffffffffffffffff8211156123405761233f611842565b5b602082029050602081019050919050565b600061236461235f84612325565b6118a2565b9050808382526020820190506020840283018581111561238757612386611708565b5b835b818110156123b0578061239c88826121ba565b845260208401935050602081019050612389565b5050509392505050565b600082601f8301126123cf576123ce6116fe565b5b81516123df848260208601612351565b91505092915050565b6000602082840312156123fe576123fd611660565b5b600082015167ffffffffffffffff81111561241c5761241b611665565b5b612428848285016123ba565b91505092915050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b60006124698383611fe7565b60208301905092915050565b6000602082019050919050565b600061248d82612431565b612497818561243c565b93506124a28361244d565b8060005b838110156124d35781516124ba888261245d565b97506124c583612475565b9250506001810190506124a6565b5085935050505092915050565b600060208201905081810360008301526124fa8184612482565b905092915050565b6000604082019050818103600083015261251d8185876122d4565b9050818103602083015261253181846120a9565b9050949350505050565b612544816116c8565b82525050565b600060408201905061255f6000830185611ee3565b61256c602083018461253b565b9392505050565b60008151905061258281611dee565b92915050565b60006020828403121561259e5761259d611660565b5b60006125ac84828501612573565b91505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600061261e826116c8565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036126505761264f6125e4565b5b600182019050919050565b6000612666826116c8565b9150612671836116c8565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff038211156126a6576126a56125e4565b5b828201905092915050565b7f536166654d6174683a206164646974696f6e206f766572666c6f770000000000600082015250565b60006126e7601b83611c95565b91506126f2826126b1565b602082019050919050565b60006020820190508181036000830152612716816126da565b9050919050565b6000819050919050565b600060ff82169050919050565b600061274f61274a6127458461271d565b611b3e565b612727565b9050919050565b61275f81612734565b82525050565b600060408201905061277a6000830185611ee3565b6127876020830184612756565b9392505050565b7f5361666545524332303a204552433230206f7065726174696f6e20646964206e60008201527f6f74207375636365656400000000000000000000000000000000000000000000602082015250565b60006127ea602a83611c95565b91506127f58261278e565b604082019050919050565b60006020820190508181036000830152612819816127dd565b9050919050565b7f4f704578656375746f723a20696c6c6567616c2063616c6c0000000000000000600082015250565b6000612856601883611c95565b915061286182612820565b602082019050919050565b6000602082019050818103600083015261288581612849565b9050919050565b7f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f60008201527f6e74726163740000000000000000000000000000000000000000000000000000602082015250565b60006128e8602683611c95565b91506128f38261288c565b604082019050919050565b60006020820190508181036000830152612917816128db565b9050919050565b600081905092915050565b600061293482611ff6565b61293e818561291e565b935061294e818560208601611ca6565b80840191505092915050565b60006129668284612929565b915081905092915050565b7f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000600082015250565b60006129a7601d83611c95565b91506129b282612971565b602082019050919050565b600060208201905081810360008301526129d68161299a565b905091905056fe4f704578656375746f723a206c6f772d6c6576656c2064656c656761746563616c6c206661696c6564a2646970667358221220ed9d43a5b4a9bd69ea5ac17fb51616fa6fb15f22ca68403ac5d00789beb9090064736f6c634300080f0033

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

0000000000000000000000009b4ae7b164d195df9c4da5d08be88b2848b2eada

-----Decoded View---------------
Arg [0] : _registry (address): 0x9b4Ae7b164d195df9C4Da5d08Be88b2848b2EaDA

-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 0000000000000000000000009b4ae7b164d195df9c4da5d08be88b2848b2eada


Block Transaction Difficulty 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
Loading...
Loading

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.