Contract 0x5ab3e51608cea26090445ca89bc91628c8bb99f9 4

 
Txn Hash Method
Block
From
To
Value [Txn Fee]
0x76874936ad0fe44063787fc076fb657cafa67ec50799d29da6c40e878864a0790x60a060401025295862023-06-18 20:23:48309 days 21 hrs ago0x0b5a3c04d1199283938fbe887a2c82c808aa89fb IN  Contract Creation0 ETH0.00087918 0.12374
[ Download CSV Export 
Parent Txn Hash Block From To Value
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.

Similar Match Source Code
This contract matches the deployed Bytecode of the Source Code for Contract 0x90feaf7727a6ce75f518728d296877830fd39a49
The constructor portion of the code might be different and could alter the actual behaviour of the contract

Contract Name:
OperationExecutor

Compiler Version
v0.8.15+commit.e14f2714

Optimization Enabled:
Yes with 1000 runs

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

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

File 2 of 25 : TakeFlashloan.sol
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity ^0.8.15;

import { Executable } from "../common/Executable.sol";
import { ServiceRegistry } from "../../core/ServiceRegistry.sol";
import { IVault } from "../../interfaces/balancer/IVault.sol";
import { IERC3156FlashBorrower } from "../../interfaces/flashloan/IERC3156FlashBorrower.sol";
import { IERC3156FlashLender } from "../../interfaces/flashloan/IERC3156FlashLender.sol";
import { IFlashLoanRecipient } from "../../interfaces/flashloan/balancer/IFlashLoanRecipient.sol";
import { FlashloanData, FlashloanProvider } from "../../core/types/Common.sol";
import { OPERATION_EXECUTOR, DAI, CHAINLOG_VIEWER } from "../../core/constants/Common.sol";
import { MCD_FLASH } from "../../core/constants/Maker.sol";
import { BALANCER_VAULT } from "../../core/constants/Balancer.sol";
import { ChainLogView } from "../../core/views/ChainLogView.sol";
import { ProxyPermission } from "../../libs/DS/ProxyPermission.sol";
import { IERC20 } from "../../libs/SafeERC20.sol";

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

  constructor(
    ServiceRegistry _registry,
    address _dai,
    address _dsGuardFactory
  ) ProxyPermission(_dsGuardFactory) {
    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 isProxyFlashloan flag is used to give the Operation Executor temporary authority to call the execute method on a user"s proxy. Refers to any proxy wallet (DSProxy or DPMProxy at time of writing)
   * @dev isDPMProxy flag switches between regular DSPRoxy and DPMProxy
   * @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.isProxyFlashloan) {
      givePermission(flData.isDPMProxy, operationExecutorAddress);
    }

    if (flData.provider == FlashloanProvider.DssFlash) {
      ChainLogView chainlogView = ChainLogView(registry.getRegisteredService(CHAINLOG_VIEWER));

      IERC3156FlashLender(chainlogView.getServiceAddress(MCD_FLASH)).flashLoan(
        IERC3156FlashBorrower(operationExecutorAddress),
        dai,
        flData.amount,
        abi.encode(flData, address(this))
      );
    }

    if (flData.provider == FlashloanProvider.Balancer) {
      IERC20[] memory tokens = new IERC20[](1);
      uint256[] memory amounts = new uint256[](1);

      tokens[0] = IERC20(flData.asset);
      amounts[0] = flData.amount;

      IVault(registry.getRegisteredService(BALANCER_VAULT)).flashLoan(
        IFlashLoanRecipient(operationExecutorAddress),
        tokens,
        amounts,
        abi.encode(flData, address(this))
      );
    }

    if (flData.isProxyFlashloan) {
      removePermission(flData.isDPMProxy, operationExecutorAddress);
    }
  }

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

File 3 of 25 : Balancer.sol
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity ^0.8.15;

string constant BALANCER_VAULT = "BalancerVault";

File 4 of 25 : Common.sol
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity ^0.8.15;

string constant OPERATION_STORAGE = "OperationStorage_2";
string constant OPERATION_EXECUTOR = "OperationExecutor_2";
string constant OPERATIONS_REGISTRY = "OperationsRegistry_2";
string constant CHAINLOG_VIEWER = "ChainLogView";
string constant ONE_INCH_AGGREGATOR = "OneInchAggregator";
string constant DS_GUARD_FACTORY = "DSGuardFactory";
string constant WETH = "WETH";
string constant DAI = "DAI";
uint256 constant RAY = 10 ** 27;
bytes32 constant NULL = "";

/**
 * @dev We do not include patch versions in contract names to allow
 * for hotfixes of Action dma-contracts
 * and to limit updates to TheGraph
 * if the types encoded in emitted events change then use a minor version and
 * update the ServiceRegistry with a new entry
 * and update TheGraph decoding accordingly
 */
string constant POSITION_CREATED_ACTION = "PositionCreated";

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

address constant ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;

File 5 of 25 : Maker.sol
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity ^0.8.15;

string constant FLASH_MINT_MODULE = "McdFlashMintModule";

string constant MCD_MANAGER = "McdManager";
string constant MCD_JUG = "McdJug";
string constant MCD_JOIN_DAI = "McdJoinDai";

string constant MCD_FLASH = "MCD_FLASH";

File 6 of 25 : OperationExecutor.sol
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity ^0.8.15;

import { ServiceRegistry } from "./ServiceRegistry.sol";
import { OperationStorage } from "./OperationStorage.sol";
import { OperationsRegistry } from "./OperationsRegistry.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 { IFlashLoanRecipient } from "../interfaces/flashloan/balancer/IFlashLoanRecipient.sol";
import { IDSProxy } from "../interfaces/ds/IDSProxy.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";
import { BALANCER_VAULT } from "./constants/Balancer.sol";

error UntrustedLender(address lender);
error InconsistentAsset(address flashloaned, address required);
error InconsistentAmount(uint256 flashloaned, uint256 required);

/**
 * @title Operation Executor
 * @notice Is responsible for executing sequences of Actions (Operations)
 */
contract OperationExecutor is IERC3156FlashBorrower, IFlashLoanRecipient {
  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(bytes32 indexed 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();
    (bytes32[] memory actions, bool[] memory optional) = opRegistry.getOperation(operationName);
    opStorage.setOperationActions(actions, optional);
    aggregate(calls);

    opStorage.clearStorage();
    opStorage.unlock();
    // By packing the string into bytes32 which means the max char length is capped at 64
    emit Operation(bytes32(abi.encodePacked(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, calls[current].skipped);
      }
      if (!calls[current].skipped) {
        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 isProxyFlashloan 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 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) {
    FlashloanData memory flData = abi.decode(data, (FlashloanData));
    address lender = registry.getRegisteredService(FLASH_MINT_MODULE);

    checkIfLenderIsTrusted(lender);
    checkIfFlashloanedAssetIsTheRequiredOne(asset, flData.asset);
    checkIfFlashloanedAmountIsTheRequiredOne(asset, flData.amount);

    processFlashloan(flData, initiator);

    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");
  }

  function receiveFlashLoan(
    IERC20[] memory tokens,
    uint256[] memory amounts,
    uint256[] memory feeAmounts,
    bytes memory data
  ) external override {
    address asset = address(tokens[0]);
    address lender = registry.getRegisteredService(BALANCER_VAULT);
    (FlashloanData memory flData, address initiator) = abi.decode(data, (FlashloanData, address));

    checkIfLenderIsTrusted(lender);
    checkIfFlashloanedAssetIsTheRequiredOne(asset, flData.asset);
    checkIfFlashloanedAmountIsTheRequiredOne(asset, flData.amount);

    processFlashloan(flData, initiator);

    uint256 paybackAmount = amounts[0].add(feeAmounts[0]);

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

    IERC20(asset).safeTransfer(lender, paybackAmount);
  }

  function checkIfLenderIsTrusted(address lender) public view {
    if (msg.sender != lender) revert UntrustedLender(msg.sender);
  }

  function checkIfFlashloanedAssetIsTheRequiredOne(
    address flashloaned,
    address required
  ) public pure {
    if (flashloaned != required) revert InconsistentAsset(flashloaned, required);
  }

  function checkIfFlashloanedAmountIsTheRequiredOne(
    address asset,
    uint256 requiredAmount
  ) public view {
    uint256 assetBalance = IERC20(asset).balanceOf(address(this));
    if (assetBalance < requiredAmount) revert InconsistentAmount(assetBalance, requiredAmount);
  }

  function processFlashloan(FlashloanData memory flData, address initiator) private {
    if (flData.isProxyFlashloan) {
      IERC20(flData.asset).safeTransfer(initiator, flData.amount);
      IDSProxy(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);
    }
  }
}

File 7 of 25 : OperationsRegistry.sol
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity ^0.8.15;

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

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

/**
 * @title Operation Registry
 * @notice Stores the Actions that constitute a given Operation and information if an Action can be skipped

 */
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(bytes32 indexed name);

  /**
   * @notice Adds an Operation's Actions keyed to a an operation name
   * @param operation Struct with Operation name, actions and their optionality
   */
  function addOperation(StoredOperation calldata operation) external onlyOwner {
    operations[operation.name] = operation;
    // By packing the string into bytes32 which means the max char length is capped at 64
    emit OperationAdded(bytes32(abi.encodePacked(operation.name)));
  }

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

File 8 of 25 : OperationStorage.sol
// SPDX-License-Identifier: AGPL-3.0-or-later
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;
  bool[] public optionals;
  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 and their optionality
   */
  function setOperationActions(bytes32[] memory _actions, bool[] memory _optionals) external {
    actions = _actions;
    optionals = _optionals;
  }

  /**
   * @param actionHash Checks the current action has against the expected action hash
   */
  function verifyAction(bytes32 actionHash, bool skipped) external {
    if (skipped) {
      require(optionals[action], "Action cannot be skipped");
    }
    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 9 of 25 : ServiceRegistry.sol
// SPDX-License-Identifier: AGPL-3.0-or-later

/// ServiceRegistry.sol

// Copyright (C) 2021-2021 Oazo Apps Limited

// 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.0;

contract ServiceRegistry {
  uint256 public constant MAX_DELAY = 30 days;

  mapping(bytes32 => uint256) public lastExecuted;
  mapping(bytes32 => address) private namedService;
  mapping(bytes32 => bool) private invalidHashes;
  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;
  }

  function transferOwnership(
    address newOwner
  ) external onlyOwner validateInput(36) delayedExecution {
    owner = newOwner;
  }

  function changeRequiredDelay(
    uint256 newDelay
  ) external onlyOwner validateInput(36) delayedExecution {
    require(newDelay <= MAX_DELAY, "registry/invalid-delay");
    requiredDelay = newDelay;
  }

  function getServiceNameHash(string memory name) external pure returns (bytes32) {
    return keccak256(abi.encodePacked(name));
  }

  function addNamedService(
    bytes32 serviceNameHash,
    address serviceAddress
  ) external onlyOwner validateInput(68) delayedExecution {
    require(invalidHashes[serviceNameHash] == false, "registry/service-name-used-before");
    require(namedService[serviceNameHash] == address(0), "registry/service-override");
    namedService[serviceNameHash] = serviceAddress;
    emit NamedServiceAdded(serviceNameHash, serviceAddress);
  }

  function removeNamedService(bytes32 serviceNameHash) external onlyOwner validateInput(36) {
    require(namedService[serviceNameHash] != address(0), "registry/service-does-not-exist");
    namedService[serviceNameHash] = address(0);
    invalidHashes[serviceNameHash] = true;
    emit NamedServiceRemoved(serviceNameHash);
  }

  function getRegisteredService(string memory serviceName) external view returns (address) {
    return namedService[keccak256(abi.encodePacked(serviceName))];
  }

  function getServiceAddress(bytes32 serviceNameHash) external view returns (address) {
    return namedService[serviceNameHash];
  }

  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);
  event NamedServiceAdded(bytes32 nameHash, address service);
}

File 10 of 25 : Common.sol
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity ^0.8.15;

enum FlashloanProvider {
  DssFlash,
  Balancer
}

struct FlashloanData {
  uint256 amount;
  address asset;
  bool isProxyFlashloan;
  bool isDPMProxy;
  FlashloanProvider provider;
  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;
  bool sumAmounts;
}

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

struct Call {
  bytes32 targetHash;
  bytes callData;
  bool skipped;
}

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

struct WrapEthData {
  uint256 amount;
}

struct UnwrapEthData {
  uint256 amount;
}

struct ReturnFundsData {
  address asset;
}

struct PositionCreatedData {
  string protocol;
  string positionType;
  address collateralToken;
  address debtToken;
}

File 11 of 25 : ChainLogView.sol
//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.15;

import { IChainLog } from "../../interfaces/maker/IChainLog.sol";

/**
 * @title ChainLogView
 * @notice Reads the Chainlog contract to get the address of a service by its name
 */
contract ChainLogView {
  address public immutable chainlogAddress;

  constructor(address _chainlogAddress) {
    chainlogAddress = _chainlogAddress;
  }

  /**
   * @notice Gets the string representation of a bytes32 value with `-` replaced with `_`
   * @param _bytes32 value to decode to string
   * @return The decoded string
   */
  function bytes32ToString(bytes32 _bytes32) public pure returns (string memory) {
    uint8 i = 0;
    while (i < 32 && _bytes32[i] != 0) {
      i++;
    }
    bytes memory bytesArray = new bytes(i);
    for (i = 0; i < 32 && _bytes32[i] != 0; i++) {
      if (_bytes32[i] == bytes1("-")) {
        bytesArray[i] = bytes1("_");
      } else {
        bytesArray[i] = _bytes32[i];
      }
    }
    return string(bytesArray);
  }

  /**
   * @notice Gets the address of a service by its name
   * @param serviceName The name of the service
   * @return The address of the service
   */

  function getServiceAddress(string calldata serviceName) public view returns (address) {
    bytes32 serviceHash = bytes32(abi.encodePacked(serviceName));
    return IChainLog(chainlogAddress).getAddress(serviceHash);
  }

  /**
   * @notice Gets the address of a join adapter by its ilk name
   * @param ilkName The name of the ilk
   * @return The address of the join adapter
   */
  function getIlkJoinAddressByName(string calldata ilkName) public view returns (address) {
    bytes32 ilkHash = bytes32(abi.encodePacked("MCD_JOIN_", ilkName));
    return IChainLog(chainlogAddress).getAddress(ilkHash);
  }

  /**
   * @notice Gets the address of a join adapter by its ilk hash
   * @param ilkHash The hash of the ilk name
   * @return The address of the join adapter
   */
  function getIlkJoinAddressByHash(bytes32 ilkHash) public view returns (address) {
    bytes32 newIlkHash = bytes32(abi.encodePacked("MCD_JOIN_", bytes32ToString(ilkHash)));
    return IChainLog(chainlogAddress).getAddress(newIlkHash);
  }
}

File 12 of 25 : IVault.sol
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity ^0.8.15;

import { IFlashLoanRecipient } from "../flashloan/balancer/IFlashLoanRecipient.sol";
import { IERC20 } from "../../libs/SafeERC20.sol";

interface IVault {
  function flashLoan(
    IFlashLoanRecipient recipient,
    IERC20[] memory tokens,
    uint256[] memory amounts,
    bytes memory userData
  ) external;
}

File 13 of 25 : IAccountGuard.sol
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity >=0.8.15;

interface IAccountGuard {
  function owners(address) external view returns (address);

  function owner() external view returns (address);

  function setWhitelist(address target, bool status) external;

  function canCall(address proxy, address operator) external view returns (bool);

  function permit(address caller, address target, bool allowance) external;

  function isWhitelisted(address target) external view returns (bool);

  function isWhitelistedSend(address target) external view returns (bool);
}

File 14 of 25 : IAccountImplementation.sol
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity >=0.8.15;

interface IAccountImplementation {
  function execute(address _target, bytes memory _data) external payable returns (bytes32 response);

  function send(address _target, bytes memory _data) external payable;

  function owner() external view returns (address owner);

  function guard() external returns (address);
}

File 15 of 25 : IDSProxy.sol
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity ^0.8.15;

interface IDSProxy {
  function owner() external returns (address);

  function execute(bytes memory, bytes memory) external payable returns (address, bytes memory);

  function execute(address, bytes memory) external payable returns (bytes memory);

  function setCache(address _cacheAddr) external returns (bool);
}

interface IDSAuthority {
  function canCall(address, address, bytes4) external view returns (bool);
}

interface IDSAuth {
  function authority() external returns (IDSAuthority);

  function setAuthority(IDSAuthority) external;
}

interface IDSGuard {
  function canCall(address, address, bytes4) external view returns (bool);

  function permit(address, address, bytes32) external;

  function forbid(address, address, bytes32) external;
}

interface IDSGuardFactory {
  function newGuard() external returns (IDSGuard);
}

File 16 of 25 : IFlashLoanRecipient.sol
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity ^0.8.15;

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

interface IFlashLoanRecipient {
  /**
   * @dev When `flashLoan` is called on the Vault, it invokes the `receiveFlashLoan` hook on the recipient.
   *
   * At the time of the call, the Vault will have transferred `amounts` for `tokens` to the recipient. Before this
   * call returns, the recipient must have transferred `amounts` plus `feeAmounts` for each token back to the
   * Vault, or else the entire flash loan will revert.
   *
   * `userData` is the same value passed in the `IVault.flashLoan` call.
   */
  function receiveFlashLoan(
    IERC20[] memory tokens,
    uint256[] memory amounts,
    uint256[] memory feeAmounts,
    bytes memory userData
  ) external;
}

File 17 of 25 : 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 18 of 25 : 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 19 of 25 : IChainLog.sol
// SPDX-License-Identifier: AGPL-3.0-or-later

pragma solidity ^0.8.15;

abstract contract IChainLog {
  function getAddress(bytes32 _key) public view virtual returns (address addr);
}

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

File 21 of 25 : ActionAddress.sol
// SPDX-License-Identifier: AGPL-3.0-or-later
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 22 of 25 : 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 23 of 25 : ProxyPermission.sol
//SPDX-License-Identifier: Unlicense

pragma solidity ^0.8.15;

import { FlashloanData } from "../../core/types/Common.sol";
import { IAccountImplementation } from "../../interfaces/dpm/IAccountImplementation.sol";
import { IAccountGuard } from "../../interfaces/dpm/IAccountGuard.sol";
import { ServiceRegistry } from "../../core/ServiceRegistry.sol";
import { DS_GUARD_FACTORY } from "../../core/constants/Common.sol";
import { IDSGuardFactory, IDSGuard, IDSAuth, IDSAuthority } from "../../interfaces/ds/IDSProxy.sol";

contract ProxyPermission {
  IDSGuardFactory internal immutable dsGuardFactory;
  bytes4 public constant ALLOWED_METHOD_HASH = bytes4(keccak256("execute(address,bytes)"));

  constructor(address _dsGuardFactory) {
    dsGuardFactory = IDSGuardFactory(_dsGuardFactory);
  }

  function givePermission(bool isDPMProxy, address _contractAddr) public {
    if (isDPMProxy) {
      // DPM permission
      IAccountGuard(IAccountImplementation(address(this)).guard()).permit(
        _contractAddr,
        address(this),
        true
      );
    } else {
      // DSProxy permission
      address currAuthority = address(IDSAuth(address(this)).authority());
      IDSGuard guard = IDSGuard(currAuthority);
      if (currAuthority == address(0)) {
        guard = dsGuardFactory.newGuard();
        IDSAuth(address(this)).setAuthority(IDSAuthority(address(guard)));
      }

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

  function removePermission(bool isDPMProxy, address _contractAddr) public {
    if (isDPMProxy) {
      // DPM permission
      IAccountGuard(IAccountImplementation(address(this)).guard()).permit(
        _contractAddr,
        address(this),
        false
      );
    } else {
      // DSProxy permission
      address currAuthority = address(IDSAuth(address(this)).authority());
      if (currAuthority == address(0)) {
        return;
      }
      IDSGuard guard = IDSGuard(currAuthority);
      guard.forbid(_contractAddr, address(this), ALLOWED_METHOD_HASH);
    }
  }
}

File 24 of 25 : 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 25 of 25 : 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;
  }
}

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

Contract ABI

[{"inputs":[{"internalType":"contract ServiceRegistry","name":"_registry","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"flashloaned","type":"uint256"},{"internalType":"uint256","name":"required","type":"uint256"}],"name":"InconsistentAmount","type":"error"},{"inputs":[{"internalType":"address","name":"flashloaned","type":"address"},{"internalType":"address","name":"required","type":"address"}],"name":"InconsistentAsset","type":"error"},{"inputs":[{"internalType":"address","name":"lender","type":"address"}],"name":"UntrustedLender","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"name","type":"bytes32"},{"components":[{"internalType":"bytes32","name":"targetHash","type":"bytes32"},{"internalType":"bytes","name":"callData","type":"bytes"},{"internalType":"bool","name":"skipped","type":"bool"}],"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":"bool","name":"skipped","type":"bool"}],"internalType":"struct Call[]","name":"calls","type":"tuple[]"}],"name":"callbackAggregate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"requiredAmount","type":"uint256"}],"name":"checkIfFlashloanedAmountIsTheRequiredOne","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"flashloaned","type":"address"},{"internalType":"address","name":"required","type":"address"}],"name":"checkIfFlashloanedAssetIsTheRequiredOne","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"lender","type":"address"}],"name":"checkIfLenderIsTrusted","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32","name":"targetHash","type":"bytes32"},{"internalType":"bytes","name":"callData","type":"bytes"},{"internalType":"bool","name":"skipped","type":"bool"}],"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":[{"internalType":"contract IERC20[]","name":"tokens","type":"address[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"internalType":"uint256[]","name":"feeAmounts","type":"uint256[]"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"receiveFlashLoan","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"registry","outputs":[{"internalType":"contract ServiceRegistry","name":"","type":"address"}],"stateMutability":"view","type":"function"}]

Deployed Bytecode

0x60806040526004361061007b5760003560e01c8063b7c9c69f1161004e578063b7c9c69f14610141578063c8d78f5214610161578063f04f270714610181578063f1298ed7146101a157600080fd5b806323e30c8b146100805780633fbeae9f146100b357806360ac8fe6146100d55780637b103999146100f5575b600080fd5b34801561008c57600080fd5b506100a061009b3660046117f2565b6101b4565b6040519081526020015b60405180910390f35b3480156100bf57600080fd5b506100d36100ce36600461186e565b6103c7565b005b3480156100e157600080fd5b506100d36100f03660046118a7565b610429565b34801561010157600080fd5b506101297f000000000000000000000000f22f17b1d2354b4f4f52e4d164e4eb5e1f0a6ba681565b6040516001600160a01b0390911681526020016100aa565b34801561014d57600080fd5b506100d361015c3660046118c4565b610470565b34801561016d57600080fd5b506100d361017c366004611b15565b610526565b34801561018d57600080fd5b506100d361019c366004611ba5565b61064d565b6100d36101af366004611cb0565b61089f565b6000806101c383850185611d36565b604080518082018252601281527f4d6364466c6173684d696e744d6f64756c65000000000000000000000000000060208201529051630851f3bd60e01b81529192506000916001600160a01b037f000000000000000000000000f22f17b1d2354b4f4f52e4d164e4eb5e1f0a6ba61691630851f3bd916102469190600401611e4f565b602060405180830381865afa158015610263573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102879190611e62565b905061029281610429565b6102a08883602001516103c7565b6102ae888360000151610470565b6102b8828a610ccc565b60006102c48888610f00565b6040516370a0823160e01b815230600482015290915081906001600160a01b038b16906370a0823190602401602060405180830381865afa15801561030d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103319190611e7f565b10156103845760405162461bcd60e51b815260206004820152601e60248201527f496e73756666696369656e742066756e647320666f72207061796261636b000060448201526064015b60405180910390fd5b6103986001600160a01b038a168383610f66565b507f439148f0bbc682ca079e46d6e2c2f0c1e3b820f1a291b069d8882abf8cf18dd99998505050505050505050565b806001600160a01b0316826001600160a01b031614610425576040517f9f2334ed0000000000000000000000000000000000000000000000000000000081526001600160a01b0380841660048301528216602482015260440161037b565b5050565b336001600160a01b0382161461046d576040517fe9048e8c00000000000000000000000000000000000000000000000000000000815233600482015260240161037b565b50565b6040516370a0823160e01b81523060048201526000906001600160a01b038416906370a0823190602401602060405180830381865afa1580156104b7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104db9190611e7f565b905081811015610521576040517f3afe323f000000000000000000000000000000000000000000000000000000008152600481018290526024810183905260440161037b565b505050565b604080518082018252601381527f4f7065726174696f6e4578656375746f725f320000000000000000000000000060208201529051630851f3bd60e01b81526001600160a01b037f000000000000000000000000f22f17b1d2354b4f4f52e4d164e4eb5e1f0a6ba61691630851f3bd916105a39190600401611e4f565b602060405180830381865afa1580156105c0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105e49190611e62565b6001600160a01b0316336001600160a01b0316146106445760405162461bcd60e51b815260206004820152601c60248201527f4f704578656375746f723a2043616c6c657220666f7262696464656e00000000604482015260640161037b565b61046d81611011565b60008460008151811061066257610662611e98565b6020026020010151905060007f000000000000000000000000f22f17b1d2354b4f4f52e4d164e4eb5e1f0a6ba66001600160a01b0316630851f3bd6040518060400160405280600d81526020017f42616c616e6365725661756c74000000000000000000000000000000000000008152506040518263ffffffff1660e01b81526004016106ef9190611e4f565b602060405180830381865afa15801561070c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107309190611e62565b9050600080848060200190518101906107499190611fe0565b9150915061075683610429565b6107648483602001516103c7565b610772848360000151610470565b61077c8282610ccc565b60006107c58760008151811061079457610794611e98565b6020026020010151896000815181106107af576107af611e98565b6020026020010151610f0090919063ffffffff16565b6040516370a0823160e01b815230600482015290915081906001600160a01b038716906370a0823190602401602060405180830381865afa15801561080e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108329190611e7f565b10156108805760405162461bcd60e51b815260206004820152601e60248201527f496e73756666696369656e742066756e647320666f72207061796261636b0000604482015260640161037b565b6108946001600160a01b038616858361130b565b505050505050505050565b604080518082018252601281527127b832b930ba34b7b729ba37b930b3b2af9960711b60208201529051630851f3bd60e01b81526000916001600160a01b037f000000000000000000000000f22f17b1d2354b4f4f52e4d164e4eb5e1f0a6ba61691630851f3bd9161091391600401611e4f565b602060405180830381865afa158015610930573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109549190611e62565b9050806001600160a01b031663f83d08ba6040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561099157600080fd5b505af11580156109a5573d6000803e3d6000fd5b5050604080518082018252601481527f4f7065726174696f6e7352656769737472795f3200000000000000000000000060208201529051630851f3bd60e01b8152600093506001600160a01b037f000000000000000000000000f22f17b1d2354b4f4f52e4d164e4eb5e1f0a6ba6169250630851f3bd91610a2891600401611e4f565b602060405180830381865afa158015610a45573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a699190611e62565b9050816001600160a01b03166369bd38a06040518163ffffffff1660e01b8152600401600060405180830381600087803b158015610aa657600080fd5b505af1158015610aba573d6000803e3d6000fd5b50505050600080826001600160a01b0316631fffb05c87876040518363ffffffff1660e01b8152600401610aef9291906120b3565b600060405180830381865afa158015610b0c573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610b349190810190612146565b6040517f42a4b81100000000000000000000000000000000000000000000000000000000815291935091506001600160a01b038516906342a4b81190610b809085908590600401612202565b600060405180830381600087803b158015610b9a57600080fd5b505af1158015610bae573d6000803e3d6000fd5b50505050610bbb87611011565b836001600160a01b03166369bd38a06040518163ffffffff1660e01b8152600401600060405180830381600087803b158015610bf657600080fd5b505af1158015610c0a573d6000803e3d6000fd5b50505050836001600160a01b031663a69df4b56040518163ffffffff1660e01b8152600401600060405180830381600087803b158015610c4957600080fd5b505af1158015610c5d573d6000803e3d6000fd5b505050508585604051602001610c7492919061227f565b604051602081830303815290604052610c8c9061228f565b7f71715266c730dfceca3e44620faebf315f8e7d7404b2de5f3121d0d87c17832c88604051610cbb91906122b6565b60405180910390a250505050505050565b816040015115610dc65781516020830151610cf4916001600160a01b0390911690839061130b565b806001600160a01b0316631cff79cd3063c8d78f5260e01b8560a00151604051602401610d2191906122b6565b60408051601f198184030181529181526020820180516001600160e01b03167fffffffff000000000000000000000000000000000000000000000000000000009485161790525160e085901b9092168252610d7f9291600401612339565b6000604051808303816000875af1158015610d9e573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610521919081019061235b565b604080518082018252601281527127b832b930ba34b7b729ba37b930b3b2af9960711b60208201529051630851f3bd60e01b81526000916001600160a01b037f000000000000000000000000f22f17b1d2354b4f4f52e4d164e4eb5e1f0a6ba61691630851f3bd91610e3a91600401611e4f565b602060405180830381865afa158015610e57573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e7b9190611e62565b6040517fd59dfd610000000000000000000000000000000000000000000000000000000081526001600160a01b0384811660048301529192509082169063d59dfd6190602401600060405180830381600087803b158015610edb57600080fd5b505af1158015610eef573d6000803e3d6000fd5b505050506105218360a00151611011565b600080610f0d83856123a6565b905083811015610f5f5760405162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015260640161037b565b9392505050565b6040516001600160a01b038316602482015260006044820152610fe190849063095ea7b360e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03167fffffffff0000000000000000000000000000000000000000000000000000000090931692909217909152611354565b6040516001600160a01b03831660248201526044810182905261052190849063095ea7b360e01b90606401610f92565b604080518082018252601281527127b832b930ba34b7b729ba37b930b3b2af9960711b60208201529051630851f3bd60e01b81526000916001600160a01b037f000000000000000000000000f22f17b1d2354b4f4f52e4d164e4eb5e1f0a6ba61691630851f3bd9161108591600401611e4f565b602060405180830381865afa1580156110a2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110c69190611e62565b90506000816001600160a01b0316634b1824a46040518163ffffffff1660e01b8152600401602060405180830381865afa158015611108573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061112c91906123be565b905060005b83518110156113055781156111e657826001600160a01b031663e7acf55a85838151811061116157611161611e98565b60200260200101516000015186848151811061117f5761117f611e98565b6020026020010151604001516040518363ffffffff1660e01b81526004016111b39291909182521515602082015260400190565b600060405180830381600087803b1580156111cd57600080fd5b505af11580156111e1573d6000803e3d6000fd5b505050505b8381815181106111f8576111f8611e98565b6020026020010151604001516112f35760007f000000000000000000000000f22f17b1d2354b4f4f52e4d164e4eb5e1f0a6ba66001600160a01b031663c2527b3286848151811061124b5761124b611e98565b6020026020010151600001516040518263ffffffff1660e01b815260040161127591815260200190565b602060405180830381865afa158015611292573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112b69190611e62565b90506112f18583815181106112cd576112cd611e98565b602002602001015160200151826001600160a01b031661143990919063ffffffff16565b505b806112fd816123db565b915050611131565b50505050565b6040516001600160a01b0383166024820152604481018290526105219084907fa9059cbb0000000000000000000000000000000000000000000000000000000090606401610f92565b60006113a9826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166114bc9092919063ffffffff16565b80519091501561052157808060200190518101906113c791906123be565b6105215760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f74207375636365656400000000000000000000000000000000000000000000606482015260840161037b565b611442816114d3565b61148e5760405162461bcd60e51b815260206004820152601860248201527f4f704578656375746f723a20696c6c6567616c2063616c6c0000000000000000604482015260640161037b565b61052181604051806060016040528060298152602001612411602991396001600160a01b0385169190611561565b60606114cb8484600085611677565b949350505050565b6040805160048152602481019091526020810180516001600160e01b03167f85e92d9800000000000000000000000000000000000000000000000000000000179052600090819061152390611740565b9050600061153084611740565b7fffffffff000000000000000000000000000000000000000000000000000000009081169216919091149392505050565b606061156c8461175b565b6115de5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f60448201527f6e74726163740000000000000000000000000000000000000000000000000000606482015260840161037b565b600080856001600160a01b0316856040516115f991906123f4565b600060405180830381855af49150503d8060008114611634576040519150601f19603f3d011682016040523d82523d6000602084013e611639565b606091505b5091509150811561164d579150610f5f9050565b80511561165d5780518082602001fd5b8360405162461bcd60e51b815260040161037b9190611e4f565b60606116828561175b565b6116ce5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640161037b565b600080866001600160a01b031685876040516116ea91906123f4565b60006040518083038185875af1925050503d8060008114611727576040519150601f19603f3d011682016040523d82523d6000602084013e61172c565b606091505b5091509150811561164d5791506114cb9050565b6000815160000361175357506000919050565b506020015190565b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a4708181148015906114cb575050151592915050565b6001600160a01b038116811461046d57600080fd5b60008083601f8401126117bb57600080fd5b50813567ffffffffffffffff8111156117d357600080fd5b6020830191508360208285010111156117eb57600080fd5b9250929050565b60008060008060008060a0878903121561180b57600080fd5b863561181681611794565b9550602087013561182681611794565b94506040870135935060608701359250608087013567ffffffffffffffff81111561185057600080fd5b61185c89828a016117a9565b979a9699509497509295939492505050565b6000806040838503121561188157600080fd5b823561188c81611794565b9150602083013561189c81611794565b809150509250929050565b6000602082840312156118b957600080fd5b8135610f5f81611794565b600080604083850312156118d757600080fd5b82356118e281611794565b946020939093013593505050565b634e487b7160e01b600052604160045260246000fd5b6040516060810167ffffffffffffffff81118282101715611929576119296118f0565b60405290565b60405160c0810167ffffffffffffffff81118282101715611929576119296118f0565b604051601f8201601f1916810167ffffffffffffffff8111828210171561197b5761197b6118f0565b604052919050565b600067ffffffffffffffff82111561199d5761199d6118f0565b5060051b60200190565b600067ffffffffffffffff8211156119c1576119c16118f0565b50601f01601f191660200190565b600082601f8301126119e057600080fd5b81356119f36119ee826119a7565b611952565b818152846020838601011115611a0857600080fd5b816020850160208301376000918101602001919091529392505050565b801515811461046d57600080fd5b600082601f830112611a4457600080fd5b81356020611a546119ee83611983565b82815260059290921b84018101918181019086841115611a7357600080fd5b8286015b84811015611b0a57803567ffffffffffffffff80821115611a985760008081fd5b908801906060828b03601f1901811315611ab25760008081fd5b611aba611906565b87840135815260408085013584811115611ad45760008081fd5b611ae28e8b838901016119cf565b838b0152509382013593611af585611a25565b81019390935250508352918301918301611a77565b509695505050505050565b600060208284031215611b2757600080fd5b813567ffffffffffffffff811115611b3e57600080fd5b6114cb84828501611a33565b600082601f830112611b5b57600080fd5b81356020611b6b6119ee83611983565b82815260059290921b84018101918181019086841115611b8a57600080fd5b8286015b84811015611b0a5780358352918301918301611b8e565b60008060008060808587031215611bbb57600080fd5b843567ffffffffffffffff80821115611bd357600080fd5b818701915087601f830112611be757600080fd5b81356020611bf76119ee83611983565b82815260059290921b8401810191818101908b841115611c1657600080fd5b948201945b83861015611c3d578535611c2e81611794565b82529482019490820190611c1b565b98505088013592505080821115611c5357600080fd5b611c5f88838901611b4a565b94506040870135915080821115611c7557600080fd5b611c8188838901611b4a565b93506060870135915080821115611c9757600080fd5b50611ca4878288016119cf565b91505092959194509250565b600080600060408486031215611cc557600080fd5b833567ffffffffffffffff80821115611cdd57600080fd5b611ce987838801611a33565b94506020860135915080821115611cff57600080fd5b50611d0c868287016117a9565b9497909650939450505050565b6002811061046d57600080fd5b8035611d3181611d19565b919050565b600060208284031215611d4857600080fd5b813567ffffffffffffffff80821115611d6057600080fd5b9083019060c08286031215611d7457600080fd5b611d7c61192f565b823581526020830135611d8e81611794565b60208201526040830135611da181611a25565b60408201526060830135611db481611a25565b6060820152611dc560808401611d26565b608082015260a083013582811115611ddc57600080fd5b611de887828601611a33565b60a08301525095945050505050565b60005b83811015611e12578181015183820152602001611dfa565b838111156113055750506000910152565b60008151808452611e3b816020860160208601611df7565b601f01601f19169290920160200192915050565b602081526000610f5f6020830184611e23565b600060208284031215611e7457600080fd5b8151610f5f81611794565b600060208284031215611e9157600080fd5b5051919050565b634e487b7160e01b600052603260045260246000fd5b8051611d3181611d19565b600082601f830112611eca57600080fd5b8151611ed86119ee826119a7565b818152846020838601011115611eed57600080fd5b6114cb826020830160208701611df7565b600082601f830112611f0f57600080fd5b81516020611f1f6119ee83611983565b82815260059290921b84018101918181019086841115611f3e57600080fd5b8286015b84811015611b0a57805167ffffffffffffffff80821115611f635760008081fd5b908801906060828b03601f1901811315611f7d5760008081fd5b611f85611906565b87840151815260408085015184811115611f9f5760008081fd5b611fad8e8b83890101611eb9565b838b0152509382015193611fc085611a25565b81019390935250508352918301918301611f42565b8051611d3181611794565b60008060408385031215611ff357600080fd5b825167ffffffffffffffff8082111561200b57600080fd5b9084019060c0828703121561201f57600080fd5b61202761192f565b82518152602083015161203981611794565b6020820152604083015161204c81611a25565b6040820152606083015161205f81611a25565b606082015261207060808401611eae565b608082015260a08301518281111561208757600080fd5b61209388828601611efe565b60a08301525093506120aa91505060208401611fd5565b90509250929050565b60208152816020820152818360408301376000818301604090810191909152601f909201601f19160101919050565b600082601f8301126120f357600080fd5b815160206121036119ee83611983565b82815260059290921b8401810191818101908684111561212257600080fd5b8286015b84811015611b0a57805161213981611a25565b8352918301918301612126565b6000806040838503121561215957600080fd5b825167ffffffffffffffff8082111561217157600080fd5b818501915085601f83011261218557600080fd5b815160206121956119ee83611983565b82815260059290921b840181019181810190898411156121b457600080fd5b948201945b838610156121d2578551825294820194908201906121b9565b918801519196509093505050808211156121eb57600080fd5b506121f8858286016120e2565b9150509250929050565b604080825283519082018190526000906020906060840190828701845b8281101561223b5781518452928401929084019060010161221f565b5050508381038285015284518082528583019183019060005b81811015612272578351151583529284019291840191600101612254565b5090979650505050505050565b8183823760009101908152919050565b805160208083015191908110156122b0576000198160200360031b1b821691505b50919050565b60006020808301818452808551808352604092508286019150828160051b87010184880160005b8381101561232b57603f19898403018552815160608151855288820151818a87015261230b82870182611e23565b9289015115159589019590955250948701949250908601906001016122dd565b509098975050505050505050565b6001600160a01b03831681526040602082015260006114cb6040830184611e23565b60006020828403121561236d57600080fd5b815167ffffffffffffffff81111561238457600080fd5b6114cb84828501611eb9565b634e487b7160e01b600052601160045260246000fd5b600082198211156123b9576123b9612390565b500190565b6000602082840312156123d057600080fd5b8151610f5f81611a25565b6000600182016123ed576123ed612390565b5060010190565b60008251612406818460208701611df7565b919091019291505056fe4f704578656375746f723a206c6f772d6c6576656c2064656c656761746563616c6c206661696c6564a264697066735822122035e99a2c8eda1790e3749d97c3767ce3469099a7dd2481fe53e00a1781a7afd564736f6c634300080f0033

Block Transaction Gas Used Reward
Age Block Fee Address BC Fee Address Voting Power Jailed Incoming
Block Uncle Number Difficulty Gas Used Reward
Loading
Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for interesting conversations.