ETH Price: $3,108.46 (-4.86%)
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00
Transaction Hash
Method
Block
From
To
Batch Payments217561972025-02-02 3:37:475 hrs ago1738467467IN
0x3cF63891...8cd59C03f
0 ETH0.002474974.66237012
Batch Payments217523302025-02-01 14:40:4718 hrs ago1738420847IN
0x3cF63891...8cd59C03f
0 ETH0.002538623.58478482
Batch Payments217523062025-02-01 14:35:5918 hrs ago1738420559IN
0x3cF63891...8cd59C03f
0 ETH0.008054293.58956631
Batch Payments217461312025-01-31 17:54:5939 hrs ago1738346099IN
0x3cF63891...8cd59C03f
0 ETH0.002444017.49453363
Batch Payments217458222025-01-31 16:52:5940 hrs ago1738342379IN
0x3cF63891...8cd59C03f
0 ETH0.0135884311.75596105
Batch Payments217442832025-01-31 11:42:5945 hrs ago1738323779IN
0x3cF63891...8cd59C03f
0 ETH0.001129263.71127553
Batch Payments217441222025-01-31 11:10:1145 hrs ago1738321811IN
0x3cF63891...8cd59C03f
0 ETH0.001213423.62198659
Batch Payments217441042025-01-31 11:06:3545 hrs ago1738321595IN
0x3cF63891...8cd59C03f
0 ETH0.000330192.72275689
Batch Payments217440992025-01-31 11:05:3545 hrs ago1738321535IN
0x3cF63891...8cd59C03f
0 ETH0.000301762.48863777
Batch Payments217439662025-01-31 10:38:4746 hrs ago1738319927IN
0x3cF63891...8cd59C03f
0 ETH0.000771682.08388655
Batch Payments217432272025-01-31 8:09:592 days ago1738310999IN
0x3cF63891...8cd59C03f
0 ETH0.000460083.10634017
Batch Payments217425622025-01-31 5:56:112 days ago1738302971IN
0x3cF63891...8cd59C03f
0 ETH0.000317991.88660015
Batch Payments217417842025-01-31 3:19:112 days ago1738293551IN
0x3cF63891...8cd59C03f
0 ETH0.000236831.34952722
Batch Payments217395452025-01-30 19:48:592 days ago1738266539IN
0x3cF63891...8cd59C03f
0 ETH0.000919635.45793057
Batch Payments217388782025-01-30 17:34:232 days ago1738258463IN
0x3cF63891...8cd59C03f
0 ETH0.004251796.99448014
Batch Payments217387762025-01-30 17:13:472 days ago1738257227IN
0x3cF63891...8cd59C03f
0 ETH0.002925617.18858357
Batch Payments217378892025-01-30 14:16:232 days ago1738246583IN
0x3cF63891...8cd59C03f
0 ETH0.000867476.05577736
Batch Payments217378642025-01-30 14:11:232 days ago1738246283IN
0x3cF63891...8cd59C03f
0 ETH0.002157756.65031551
Batch Payments217378182025-01-30 14:02:112 days ago1738245731IN
0x3cF63891...8cd59C03f
0 ETH0.00294677.04893244
Batch Payments217378022025-01-30 13:58:592 days ago1738245539IN
0x3cF63891...8cd59C03f
0 ETH0.007206686.75151239
Batch Payments217360342025-01-30 8:02:473 days ago1738224167IN
0x3cF63891...8cd59C03f
0 ETH0.000380131.826523
Batch Payments217360332025-01-30 8:02:353 days ago1738224155IN
0x3cF63891...8cd59C03f
0 ETH0.000775252.1804232
Batch Payments217359862025-01-30 7:53:113 days ago1738223591IN
0x3cF63891...8cd59C03f
7.33718178 ETH0.000504233.1
Batch Payments217344532025-01-30 2:45:593 days ago1738205159IN
0x3cF63891...8cd59C03f
0 ETH0.000429821.94176151
Batch Payments217313242025-01-29 16:17:113 days ago1738167431IN
0x3cF63891...8cd59C03f
0 ETH0.002108635.57494973
View all transactions

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Block
From
To
217359862025-01-30 7:53:113 days ago1738223591
0x3cF63891...8cd59C03f
0.21370432 ETH
217359862025-01-30 7:53:113 days ago1738223591
0x3cF63891...8cd59C03f
0.21370432 ETH
217359862025-01-30 7:53:113 days ago1738223591
0x3cF63891...8cd59C03f
7.33718178 ETH
217231842025-01-28 13:00:234 days ago1738069223
0x3cF63891...8cd59C03f
0.00965081 ETH
217231842025-01-28 13:00:234 days ago1738069223
0x3cF63891...8cd59C03f
0.00965081 ETH
217231842025-01-28 13:00:234 days ago1738069223
0x3cF63891...8cd59C03f
0.05665816 ETH
217231842025-01-28 13:00:234 days ago1738069223
0x3cF63891...8cd59C03f
0.05665816 ETH
217231842025-01-28 13:00:234 days ago1738069223
0x3cF63891...8cd59C03f
0.15112553 ETH
217231842025-01-28 13:00:234 days ago1738069223
0x3cF63891...8cd59C03f
0.15112553 ETH
216871102025-01-23 12:12:119 days ago1737634331
0x3cF63891...8cd59C03f
0.02098049 ETH
216871102025-01-23 12:12:119 days ago1737634331
0x3cF63891...8cd59C03f
0.02098049 ETH
216871102025-01-23 12:12:119 days ago1737634331
0x3cF63891...8cd59C03f
0.7203304 ETH
216645452025-01-20 8:35:3513 days ago1737362135
0x3cF63891...8cd59C03f
1.7 ETH
216645452025-01-20 8:35:3513 days ago1737362135
0x3cF63891...8cd59C03f
1.7 ETH
216229432025-01-14 13:12:1118 days ago1736860331
0x3cF63891...8cd59C03f
0.00115846 ETH
216229432025-01-14 13:12:1118 days ago1736860331
0x3cF63891...8cd59C03f
0.00115846 ETH
216229432025-01-14 13:12:1118 days ago1736860331
0x3cF63891...8cd59C03f
0.01207595 ETH
216229432025-01-14 13:12:1118 days ago1736860331
0x3cF63891...8cd59C03f
0.01207595 ETH
216229432025-01-14 13:12:1118 days ago1736860331
0x3cF63891...8cd59C03f
0.01522106 ETH
216229432025-01-14 13:12:1118 days ago1736860331
0x3cF63891...8cd59C03f
0.01522106 ETH
215369982025-01-02 13:11:1130 days ago1735823471
0x3cF63891...8cd59C03f
1 ETH
215369982025-01-02 13:11:1130 days ago1735823471
0x3cF63891...8cd59C03f
1 ETH
215189872024-12-31 0:51:2333 days ago1735606283
0x3cF63891...8cd59C03f
0.20410543 ETH
215189872024-12-31 0:51:2333 days ago1735606283
0x3cF63891...8cd59C03f
0.20410543 ETH
215189872024-12-31 0:51:2333 days ago1735606283
0x3cF63891...8cd59C03f
7.00761999 ETH
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
BatchConversionPayments

Compiler Version
v0.8.9+commit.e5eed63a

Optimization Enabled:
No with 200 runs

Other Settings:
default evmVersion
File 1 of 15 : BatchConversionPayments.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import './interfaces/IERC20ConversionProxy.sol';
import './interfaces/IEthConversionProxy.sol';
import './BatchNoConversionPayments.sol';

/**
 * @title BatchConversionPayments
 * @notice This contract makes multiple conversion payments with references, in one transaction:
 *          - on:
 *              - ERC20 tokens: using Erc20ConversionProxy and ERC20FeeProxy
 *              - Native tokens: (e.g. ETH) using EthConversionProxy and EthereumFeeProxy
 *          - to: multiple addresses
 *          - fees: conversion proxy fees and additional batch conversion fees are paid to the same address.
 *         batchPayments is the main function to batch all kinds of payments at once.
 *         If one transaction of the batch fails, all transactions are reverted.
 * @dev batchPayments is the main function, but other batch payment functions are "public" in order to do
 *      gas optimization in some cases.
 */
contract BatchConversionPayments is BatchNoConversionPayments {
  using SafeERC20 for IERC20;

  IERC20ConversionProxy public paymentErc20ConversionProxy;
  IEthConversionProxy public paymentNativeConversionProxy;

  /** payerAuthorized is set to true to workaround the non-payable aspect in batch native conversion */
  bool private payerAuthorized = false;

  /**
   * @dev Used by the batchPayments to handle information for heterogeneous batches, grouped by payment network:
   *  - paymentNetworkId: from 0 to 4, cf. `batchPayments()` method
   *  - requestDetails all the data required for conversion and no conversion requests to be paid
   */
  struct MetaDetail {
    uint256 paymentNetworkId;
    RequestDetail[] requestDetails;
  }

  /**
   * @param _paymentErc20Proxy The ERC20 payment proxy address to use.
   * @param _paymentNativeProxy The native payment proxy address to use.
   * @param _paymentErc20ConversionProxy The ERC20 Conversion payment proxy address to use.
   * @param _paymentNativeConversionFeeProxy The native Conversion payment proxy address to use.
   * @param _chainlinkConversionPath The address of the conversion path contract.
   * @param _owner Owner of the contract.
   */
  constructor(
    address _paymentErc20Proxy,
    address _paymentNativeProxy,
    address _paymentErc20ConversionProxy,
    address _paymentNativeConversionFeeProxy,
    address _chainlinkConversionPath,
    address _owner
  )
    BatchNoConversionPayments(
      _paymentErc20Proxy,
      _paymentNativeProxy,
      _chainlinkConversionPath,
      _owner
    )
  {
    paymentErc20ConversionProxy = IERC20ConversionProxy(_paymentErc20ConversionProxy);
    paymentNativeConversionProxy = IEthConversionProxy(_paymentNativeConversionFeeProxy);
  }

  /**
   * This contract is non-payable.
   * Making a Native payment with conversion requires the contract to accept incoming Native tokens.
   * @dev See the end of `paymentNativeConversionProxy.transferWithReferenceAndFee` where the leftover is given back.
   */
  receive() external payable override {
    require(payerAuthorized || msg.value == 0, 'Non-payable');
  }

  /**
   * @notice Batch payments on different payment networks at once.
   * @param metaDetails contains paymentNetworkId and requestDetails
   * - batchMultiERC20ConversionPayments, paymentNetworkId=0
   * - batchERC20Payments, paymentNetworkId=1
   * - batchMultiERC20Payments, paymentNetworkId=2
   * - batchNativePayments, paymentNetworkId=3
   * - batchNativeConversionPayments, paymentNetworkId=4
   * If metaDetails use paymentNetworkId = 4, it must be at the end of the list, or the transaction can be reverted.
   * @param pathsToUSD The list of paths into USD for every token, used to limit the batch fees.
   *                   For batch native, mock an array of array to apply the limit, e.g: [[]]
   *                   Without paths, there is not limitation, neither for the batch native functions.
   * @param feeAddress The address where fees should be paid.
   * @dev Use pathsToUSD only if you are pretty sure the batch fees will higher than the
   *      USD limit batchFeeAmountUSDLimit, because it increase gas consumption.
   *      batchPayments only reduces gas consumption when using more than a single payment network.
   *      For single payment network payments, it is more efficient to use the suited batch function.
   */
  function batchPayments(
    MetaDetail[] calldata metaDetails,
    address[][] calldata pathsToUSD,
    address feeAddress
  ) external payable {
    require(metaDetails.length < 6, 'more than 5 metaDetails');

    uint256 batchFeeAmountUSD = 0;
    for (uint256 i = 0; i < metaDetails.length; i++) {
      MetaDetail calldata metaDetail = metaDetails[i];
      if (metaDetail.paymentNetworkId == 0) {
        batchFeeAmountUSD += _batchMultiERC20ConversionPayments(
          metaDetail.requestDetails,
          batchFeeAmountUSD,
          pathsToUSD,
          feeAddress
        );
      } else if (metaDetail.paymentNetworkId == 1) {
        batchFeeAmountUSD += _batchERC20Payments(
          metaDetail.requestDetails,
          pathsToUSD,
          batchFeeAmountUSD,
          payable(feeAddress)
        );
      } else if (metaDetail.paymentNetworkId == 2) {
        batchFeeAmountUSD += _batchMultiERC20Payments(
          metaDetail.requestDetails,
          pathsToUSD,
          batchFeeAmountUSD,
          feeAddress
        );
      } else if (metaDetail.paymentNetworkId == 3) {
        if (metaDetails[metaDetails.length - 1].paymentNetworkId == 4) {
          // Set to false only if batchNativeConversionPayments is called after this function
          transferBackRemainingNativeTokens = false;
        }
        batchFeeAmountUSD += _batchNativePayments(
          metaDetail.requestDetails,
          pathsToUSD.length == 0,
          batchFeeAmountUSD,
          payable(feeAddress)
        );
        if (metaDetails[metaDetails.length - 1].paymentNetworkId == 4) {
          transferBackRemainingNativeTokens = true;
        }
      } else if (metaDetail.paymentNetworkId == 4) {
        batchFeeAmountUSD += _batchNativeConversionPayments(
          metaDetail.requestDetails,
          pathsToUSD.length == 0,
          batchFeeAmountUSD,
          payable(feeAddress)
        );
      } else {
        revert('Wrong paymentNetworkId');
      }
    }
  }

  /**
   * @notice Send a batch of ERC20 payments with amounts based on a request
   * currency (e.g. fiat), with fees and paymentReferences to multiple accounts, with multiple tokens.
   * @param requestDetails List of ERC20 requests denominated in fiat to pay.
   * @param pathsToUSD The list of paths into USD for every token, used to limit the batch fees.
   *                   Without paths, there is not a fee limitation, and it consumes less gas.
   * @param feeAddress The fee recipient.
   */
  function batchMultiERC20ConversionPayments(
    RequestDetail[] calldata requestDetails,
    address[][] calldata pathsToUSD,
    address feeAddress
  ) public returns (uint256) {
    return _batchMultiERC20ConversionPayments(requestDetails, 0, pathsToUSD, feeAddress);
  }

  /**
   * @notice Send a batch of Native conversion payments with fees and paymentReferences to multiple accounts.
   *         If one payment fails, the whole batch is reverted.
   * @param requestDetails List of native requests denominated in fiat to pay.
   * @param skipFeeUSDLimit Setting the value to true skips the USD fee limit, and reduces gas consumption.
   * @param feeAddress The fee recipient.
   * @dev It uses NativeConversionProxy (EthereumConversionProxy) to pay an invoice and fees.
   *      Please:
   *        Note that if there is not enough Native token attached to the function call,
   *        the following error is thrown: "revert paymentProxy transferExactEthWithReferenceAndFee failed"
   */
  function batchNativeConversionPayments(
    RequestDetail[] calldata requestDetails,
    bool skipFeeUSDLimit,
    address payable feeAddress
  ) public payable returns (uint256) {
    return _batchNativeConversionPayments(requestDetails, skipFeeUSDLimit, 0, feeAddress);
  }

  /**
   * @notice Send a batch of ERC20 payments with amounts based on a request
   * currency (e.g. fiat), with fees and paymentReferences to multiple accounts, with multiple tokens.
   * @param requestDetails List of ERC20 requests denominated in fiat to pay.
   * @param batchFeeAmountUSD The batch fee amount in USD already paid.
   * @param pathsToUSD The list of paths into USD for every token, used to limit the batch fees.
   *                   Without paths, there is not a fee limitation, and it consumes less gas.
   * @param feeAddress The fee recipient.
   */
  function _batchMultiERC20ConversionPayments(
    RequestDetail[] calldata requestDetails,
    uint256 batchFeeAmountUSD,
    address[][] calldata pathsToUSD,
    address feeAddress
  ) private returns (uint256) {
    Token[] memory uTokens = getUTokens(requestDetails);

    IERC20 requestedToken;
    // For each token: check allowance, transfer funds on the contract and approve the paymentProxy to spend if needed
    for (uint256 k = 0; k < uTokens.length && uTokens[k].amountAndFee > 0; k++) {
      uTokens[k].batchFeeAmount = (uTokens[k].amountAndFee * batchFee) / feeDenominator;
      requestedToken = IERC20(uTokens[k].tokenAddress);
      transferToContract(
        requestedToken,
        uTokens[k].amountAndFee,
        uTokens[k].batchFeeAmount,
        address(paymentErc20ConversionProxy)
      );
    }

    // Batch pays the requests using Erc20ConversionFeeProxy
    for (uint256 i = 0; i < requestDetails.length; i++) {
      RequestDetail calldata rD = requestDetails[i];
      paymentErc20ConversionProxy.transferFromWithReferenceAndFee(
        rD.recipient,
        rD.requestAmount,
        rD.path,
        rD.paymentReference,
        rD.feeAmount,
        feeAddress,
        rD.maxToSpend,
        rD.maxRateTimespan
      );
    }

    // Batch sends back to the payer the tokens not spent and pays the batch fee
    for (uint256 k = 0; k < uTokens.length && uTokens[k].amountAndFee > 0; k++) {
      requestedToken = IERC20(uTokens[k].tokenAddress);

      // Batch sends back to the payer the tokens not spent = excessAmount
      // excessAmount = maxToSpend - reallySpent, which is equal to the remaining tokens on the contract
      uint256 excessAmount = requestedToken.balanceOf(address(this));
      if (excessAmount > 0) {
        requestedToken.safeTransfer(msg.sender, excessAmount);
      }

      // Calculate batch fee to pay
      uint256 batchFeeToPay = ((uTokens[k].amountAndFee - excessAmount) * batchFee) /
        feeDenominator;

      (batchFeeToPay, batchFeeAmountUSD) = calculateBatchFeeToPay(
        batchFeeToPay,
        uTokens[k].tokenAddress,
        batchFeeAmountUSD,
        pathsToUSD
      );

      // Payer pays the exact batch fees amount
      require(
        safeTransferFrom(uTokens[k].tokenAddress, feeAddress, batchFeeToPay),
        'Batch fee transferFrom() failed'
      );
    }
    return batchFeeAmountUSD;
  }

  /**
   * @notice Send a batch of Native conversion payments with fees and paymentReferences to multiple accounts.
   *         If one payment fails, the whole batch is reverted.
   * @param requestDetails List of native requests denominated in fiat to pay.
   * @param skipFeeUSDLimit Setting the value to true skips the USD fee limit, and reduces gas consumption.
   * @param batchFeeAmountUSD The batch fee amount in USD already paid.
   * @param feeAddress The fee recipient.
   * @dev It uses NativeConversionProxy (EthereumConversionProxy) to pay an invoice and fees.
   *      Please:
   *        Note that if there is not enough Native token attached to the function call,
   *        the following error is thrown: "revert paymentProxy transferExactEthWithReferenceAndFee failed"
   */
  function _batchNativeConversionPayments(
    RequestDetail[] calldata requestDetails,
    bool skipFeeUSDLimit,
    uint256 batchFeeAmountUSD,
    address payable feeAddress
  ) private returns (uint256) {
    uint256 contractBalance = address(this).balance;
    payerAuthorized = true;

    // Batch contract pays the requests through nativeConversionProxy
    for (uint256 i = 0; i < requestDetails.length; i++) {
      RequestDetail calldata rD = requestDetails[i];
      paymentNativeConversionProxy.transferWithReferenceAndFee{value: address(this).balance}(
        payable(rD.recipient),
        rD.requestAmount,
        rD.path,
        rD.paymentReference,
        rD.feeAmount,
        feeAddress,
        rD.maxRateTimespan
      );
    }

    // Batch contract pays batch fee
    uint256 batchFeeToPay = (((contractBalance - address(this).balance)) * batchFee) /
      feeDenominator;

    if (skipFeeUSDLimit == false) {
      (batchFeeToPay, batchFeeAmountUSD) = calculateBatchFeeToPay(
        batchFeeToPay,
        pathsNativeToUSD[0][0],
        batchFeeAmountUSD,
        pathsNativeToUSD
      );
    }

    require(address(this).balance >= batchFeeToPay, 'Not enough funds for batch conversion fees');
    feeAddress.transfer(batchFeeToPay);

    // Batch contract transfers the remaining native tokens to the payer
    (bool sendBackSuccess, ) = payable(msg.sender).call{value: address(this).balance}('');
    require(sendBackSuccess, 'Could not send remaining funds to the payer');
    payerAuthorized = false;

    return batchFeeAmountUSD;
  }

  /*
   * Admin functions to edit the conversion proxies address and fees.
   */

  /**
   * @param _paymentErc20ConversionProxy The address of the ERC20 Conversion payment proxy to use.
   *        Update cautiously, the proxy has to match the invoice proxy.
   */
  function setPaymentErc20ConversionProxy(address _paymentErc20ConversionProxy) external onlyOwner {
    paymentErc20ConversionProxy = IERC20ConversionProxy(_paymentErc20ConversionProxy);
  }

  /**
   * @param _paymentNativeConversionProxy The address of the native Conversion payment proxy to use.
   *        Update cautiously, the proxy has to match the invoice proxy.
   */
  function setPaymentNativeConversionProxy(address _paymentNativeConversionProxy)
    external
    onlyOwner
  {
    paymentNativeConversionProxy = IEthConversionProxy(_paymentNativeConversionProxy);
  }
}

File 2 of 15 : IERC20ConversionProxy.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IERC20ConversionProxy {
  // Event to declare a conversion with a reference
  event TransferWithConversionAndReference(
    uint256 amount,
    address currency,
    bytes indexed paymentReference,
    uint256 feeAmount,
    uint256 maxRateTimespan
  );

  // Event to declare a transfer with a reference
  event TransferWithReferenceAndFee(
    address tokenAddress,
    address to,
    uint256 amount,
    bytes indexed paymentReference,
    uint256 feeAmount,
    address feeAddress
  );

  function transferFromWithReferenceAndFee(
    address _to,
    uint256 _requestAmount,
    address[] calldata _path,
    bytes calldata _paymentReference,
    uint256 _feeAmount,
    address _feeAddress,
    uint256 _maxToSpend,
    uint256 _maxRateTimespan
  ) external;
}

File 3 of 15 : IEthConversionProxy.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/**
 * @title IEthConversionProxy
 * @notice This contract converts from chainlink then swaps ETH (or native token)
 *         before paying a request thanks to a conversion payment proxy.
 *         The inheritance from ReentrancyGuard is required to perform
 *         "transferExactEthWithReferenceAndFee" on the eth-fee-proxy contract
 */
interface IEthConversionProxy {
  // Event to declare a conversion with a reference
  event TransferWithConversionAndReference(
    uint256 amount,
    address currency,
    bytes indexed paymentReference,
    uint256 feeAmount,
    uint256 maxRateTimespan
  );

  // Event to declare a transfer with a reference
  // This event is emitted by this contract from a delegate call of the payment-proxy
  event TransferWithReferenceAndFee(
    address to,
    uint256 amount,
    bytes indexed paymentReference,
    uint256 feeAmount,
    address feeAddress
  );

  /**
   * @notice Performs an ETH transfer with a reference computing the payment amount based on the request amount
   * @param _to Transfer recipient of the payement
   * @param _requestAmount Request amount
   * @param _path Conversion path
   * @param _paymentReference Reference of the payment related
   * @param _feeAmount The amount of the payment fee
   * @param _feeAddress The fee recipient
   * @param _maxRateTimespan Max time span with the oldestrate, ignored if zero
   */
  function transferWithReferenceAndFee(
    address _to,
    uint256 _requestAmount,
    address[] calldata _path,
    bytes calldata _paymentReference,
    uint256 _feeAmount,
    address _feeAddress,
    uint256 _maxRateTimespan
  ) external payable;
}

File 4 of 15 : BatchNoConversionPayments.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import './lib/SafeERC20.sol';
import '@openzeppelin/contracts/access/Ownable.sol';
import './interfaces/ERC20FeeProxy.sol';
import './interfaces/EthereumFeeProxy.sol';
import './ChainlinkConversionPath.sol';

/**
 * @title BatchNoConversionPayments
 * @notice  This contract makes multiple payments with references, in one transaction:
 *          - on: ERC20 Payment Proxy and Native (ETH) Payment Proxy of the Request Network protocol
 *          - to: multiple addresses
 *          - fees: ERC20 and Native (ETH) proxies fees are paid to the same address
 *                  An additional batch fee is paid to the same address
 *         If one transaction of the batch fail, every transactions are reverted.
 * @dev It is a clone of BatchPayment.sol, with three main modifications:
 *         - function "receive" has one other condition: payerAuthorized
 *         - fees are now divided by 10_000 instead of 1_000 in previous version
 *         - batch payment functions have new names and are now public, instead of external
 */
contract BatchNoConversionPayments is Ownable {
  using SafeERC20 for IERC20;

  IERC20FeeProxy public paymentErc20Proxy;
  IEthereumFeeProxy public paymentNativeProxy;
  ChainlinkConversionPath public chainlinkConversionPath;

  /** Used to calculate batch fees: batchFee = 30 represent 0.30% of fee */
  uint16 public batchFee;
  /** Used to calculate batch fees: divide batchFee by feeDenominator */
  uint16 internal feeDenominator = 10000;
  /** The amount of the batch fee cannot exceed a predefined amount in USD, e.g:
      batchFeeAmountUSDLimit = 150 * 1e8 represents $150 */
  uint64 public batchFeeAmountUSDLimit;

  /** transferBackRemainingNativeTokens is set to false only if the payer use batchPayments
  and call both batchNativePayments and batchNativeConversionPayments */
  bool internal transferBackRemainingNativeTokens = true;

  address public USDAddress;
  address public NativeAddress;
  address[][] public pathsNativeToUSD;

  /** Contains the address of a token, the sum of the amount and fees paid with it, and the batch fee amount */
  struct Token {
    address tokenAddress;
    uint256 amountAndFee;
    uint256 batchFeeAmount;
  }

  /**
   * @dev All the information of a request, except the feeAddress
   *   recipient: Recipient address of the payment
   *   requestAmount: Request amount, in fiat for conversion payment
   *   path: Only for conversion payment: the conversion path
   *   paymentReference: Unique reference of the payment
   *   feeAmount: The fee amount, denominated in the first currency of `path` for conversion payment
   *   maxToSpend: Only for conversion payment:
   *               Maximum amount the payer wants to spend, denominated in the last currency of `path`:
   *                it includes fee proxy but NOT the batch fees to pay
   *   maxRateTimespan: Only for conversion payment:
   *                    Max acceptable times span for conversion rates, ignored if zero
   */
  struct RequestDetail {
    address recipient;
    uint256 requestAmount;
    address[] path;
    bytes paymentReference;
    uint256 feeAmount;
    uint256 maxToSpend;
    uint256 maxRateTimespan;
  }

  /**
   * @param _paymentErc20Proxy The address to the ERC20 fee payment proxy to use.
   * @param _paymentNativeProxy The address to the Native fee payment proxy to use.
   * @param _chainlinkConversionPath The address of the conversion path contract.
   * @param _owner Owner of the contract.
   */
  constructor(
    address _paymentErc20Proxy,
    address _paymentNativeProxy,
    address _chainlinkConversionPath,
    address _owner
  ) {
    paymentErc20Proxy = IERC20FeeProxy(_paymentErc20Proxy);
    paymentNativeProxy = IEthereumFeeProxy(_paymentNativeProxy);
    chainlinkConversionPath = ChainlinkConversionPath(_chainlinkConversionPath);
    transferOwnership(_owner);
    batchFee = 0;
  }

  /**
   * This contract is non-payable.
   * @dev See the end of `paymentNativeProxy.transferWithReferenceAndFee` where the leftover is given back.
   */
  receive() external payable virtual {
    require(msg.value == 0, 'Non-payable');
  }

  /**
   * @notice Send a batch of Native token payments with fees and paymentReferences to multiple accounts.
   *         If one payment fails, the whole batch reverts.
   * @param requestDetails List of Native tokens requests to pay.
   * @param skipFeeUSDLimit Setting the value to true skips the USD fee limit, and reduces gas consumption.
   * @param feeAddress The fee recipient.
   * @dev It uses NativeFeeProxy (EthereumFeeProxy) to pay an invoice and fees with a payment reference.
   *      Make sure: msg.value >= sum(_amouts)+sum(_feeAmounts)+sumBatchFeeAmount
   */
  function batchNativePayments(
    RequestDetail[] calldata requestDetails,
    bool skipFeeUSDLimit,
    address payable feeAddress
  ) public payable returns (uint256) {
    return _batchNativePayments(requestDetails, skipFeeUSDLimit, 0, payable(feeAddress));
  }

  /**
   * @notice Send a batch of ERC20 payments with fees and paymentReferences to multiple accounts.
   * @param requestDetails List of ERC20 requests to pay, with only one ERC20 token.
   * @param pathsToUSD The list of paths into USD for every token, used to limit the batch fees.
   *                   Without paths, there is not a fee limitation, and it consumes less gas.
   * @param feeAddress The fee recipient.
   * @dev Uses ERC20FeeProxy to pay an invoice and fees, with a payment reference.
   *      Make sure this contract has enough allowance to spend the payer's token.
   *      Make sure the payer has enough tokens to pay the amount, the fee, and the batch fee.
   */
  function batchERC20Payments(
    RequestDetail[] calldata requestDetails,
    address[][] calldata pathsToUSD,
    address feeAddress
  ) public returns (uint256) {
    return _batchERC20Payments(requestDetails, pathsToUSD, 0, feeAddress);
  }

  /**
   * @notice Send a batch of ERC20 payments with fees and paymentReferences to multiple accounts, with multiple tokens.
   * @param requestDetails List of ERC20 requests to pay.
   * @param pathsToUSD The list of paths into USD for every token, used to limit the batch fees.
   *                   Without paths, there is not a fee limitation, and it consumes less gas.
   * @param feeAddress The fee recipient.
   * @dev It uses ERC20FeeProxy to pay an invoice and fees, with a payment reference.
   *      Make sure this contract has enough allowance to spend the payer's token.
   *      Make sure the payer has enough tokens to pay the amount, the fee, and the batch fee.
   */
  function batchMultiERC20Payments(
    RequestDetail[] calldata requestDetails,
    address[][] calldata pathsToUSD,
    address feeAddress
  ) public returns (uint256) {
    return _batchMultiERC20Payments(requestDetails, pathsToUSD, 0, feeAddress);
  }

  /**
   * @notice Send a batch of Native token payments with fees and paymentReferences to multiple accounts.
   *         If one payment fails, the whole batch reverts.
   * @param requestDetails List of Native tokens requests to pay.
   * @param skipFeeUSDLimit Setting the value to true skips the USD fee limit, and reduces gas consumption.
   * @param batchFeeAmountUSD The batch fee amount in USD already paid.
   * @param feeAddress The fee recipient.
   * @dev It uses NativeFeeProxy (EthereumFeeProxy) to pay an invoice and fees with a payment reference.
   *      Make sure: msg.value >= sum(_amouts)+sum(_feeAmounts)+sumBatchFeeAmount
   */
  function _batchNativePayments(
    RequestDetail[] calldata requestDetails,
    bool skipFeeUSDLimit,
    uint256 batchFeeAmountUSD,
    address payable feeAddress
  ) internal returns (uint256) {
    // amount is used to get the total amount and then used as batch fee amount
    uint256 amount = 0;

    // Batch contract pays the requests thourgh NativeFeeProxy (EthFeeProxy)
    for (uint256 i = 0; i < requestDetails.length; i++) {
      RequestDetail calldata rD = requestDetails[i];
      require(address(this).balance >= rD.requestAmount + rD.feeAmount, 'Not enough funds');
      amount += rD.requestAmount;

      paymentNativeProxy.transferWithReferenceAndFee{value: rD.requestAmount + rD.feeAmount}(
        payable(rD.recipient),
        rD.paymentReference,
        rD.feeAmount,
        payable(feeAddress)
      );
    }

    // amount is updated into batch fee amount
    amount = (amount * batchFee) / feeDenominator;
    if (skipFeeUSDLimit == false) {
      (amount, batchFeeAmountUSD) = calculateBatchFeeToPay(
        amount,
        pathsNativeToUSD[0][0],
        batchFeeAmountUSD,
        pathsNativeToUSD
      );
    }
    // Check that batch contract has enough funds to pay batch fee
    require(address(this).balance >= amount, 'Not enough funds for batch fee');
    // Batch pays batch fee
    feeAddress.transfer(amount);

    // Batch contract transfers the remaining Native tokens to the payer
    if (transferBackRemainingNativeTokens && address(this).balance > 0) {
      (bool sendBackSuccess, ) = payable(msg.sender).call{value: address(this).balance}('');
      require(sendBackSuccess, 'Could not send remaining funds to the payer');
    }
    return batchFeeAmountUSD;
  }

  /**
   * @notice Send a batch of ERC20 payments with fees and paymentReferences to multiple accounts.
   * @param requestDetails List of ERC20 requests to pay, with only one ERC20 token.
   * @param pathsToUSD The list of paths into USD for every token, used to limit the batch fees.
   *                   Without paths, there is not a fee limitation, and it consumes less gas.
   * @param batchFeeAmountUSD The batch fee amount in USD already paid.
   * @param feeAddress The fee recipient.
   * @dev Uses ERC20FeeProxy to pay an invoice and fees, with a payment reference.
   *      Make sure this contract has enough allowance to spend the payer's token.
   *      Make sure the payer has enough tokens to pay the amount, the fee, and the batch fee.
   */
  function _batchERC20Payments(
    RequestDetail[] calldata requestDetails,
    address[][] calldata pathsToUSD,
    uint256 batchFeeAmountUSD,
    address feeAddress
  ) internal returns (uint256) {
    uint256 amountAndFee = 0;
    uint256 batchFeeAmount = 0;
    for (uint256 i = 0; i < requestDetails.length; i++) {
      amountAndFee += requestDetails[i].requestAmount + requestDetails[i].feeAmount;
      batchFeeAmount += requestDetails[i].requestAmount;
    }
    batchFeeAmount = (batchFeeAmount * batchFee) / feeDenominator;

    // batchFeeToPay and batchFeeAmountUSD are updated if needed
    (batchFeeAmount, batchFeeAmountUSD) = calculateBatchFeeToPay(
      batchFeeAmount,
      requestDetails[0].path[0],
      batchFeeAmountUSD,
      pathsToUSD
    );

    IERC20 requestedToken = IERC20(requestDetails[0].path[0]);

    transferToContract(requestedToken, amountAndFee, batchFeeAmount, address(paymentErc20Proxy));

    // Payer pays batch fee amount
    require(
      safeTransferFrom(requestDetails[0].path[0], feeAddress, batchFeeAmount),
      'Batch fee transferFrom() failed'
    );

    // Batch contract pays the requests using Erc20FeeProxy
    for (uint256 i = 0; i < requestDetails.length; i++) {
      RequestDetail calldata rD = requestDetails[i];
      paymentErc20Proxy.transferFromWithReferenceAndFee(
        rD.path[0],
        rD.recipient,
        rD.requestAmount,
        rD.paymentReference,
        rD.feeAmount,
        feeAddress
      );
    }

    return batchFeeAmountUSD;
  }

  /**
   * @notice Send a batch of ERC20 payments with fees and paymentReferences to multiple accounts, with multiple tokens.
   * @param requestDetails List of ERC20 requests to pay.
   * @param pathsToUSD The list of paths into USD for every token, used to limit the batch fees.
   *                   Without paths, there is not a fee limitation, and it consumes less gas.
   * @param batchFeeAmountUSD The batch fee amount in USD already paid.
   * @param feeAddress The fee recipient.
   * @dev It uses ERC20FeeProxy to pay an invoice and fees, with a payment reference.
   *      Make sure this contract has enough allowance to spend the payer's token.
   *      Make sure the payer has enough tokens to pay the amount, the fee, and the batch fee.
   */
  function _batchMultiERC20Payments(
    RequestDetail[] calldata requestDetails,
    address[][] calldata pathsToUSD,
    uint256 batchFeeAmountUSD,
    address feeAddress
  ) internal returns (uint256) {
    Token[] memory uTokens = getUTokens(requestDetails);

    // The payer transfers tokens to the batch contract and pays batch fee
    for (uint256 i = 0; i < uTokens.length && uTokens[i].amountAndFee > 0; i++) {
      uTokens[i].batchFeeAmount = (uTokens[i].batchFeeAmount * batchFee) / feeDenominator;
      IERC20 requestedToken = IERC20(uTokens[i].tokenAddress);
      transferToContract(
        requestedToken,
        uTokens[i].amountAndFee,
        uTokens[i].batchFeeAmount,
        address(paymentErc20Proxy)
      );

      // Payer pays batch fee amount

      uint256 batchFeeToPay = uTokens[i].batchFeeAmount;

      (batchFeeToPay, batchFeeAmountUSD) = calculateBatchFeeToPay(
        batchFeeToPay,
        uTokens[i].tokenAddress,
        batchFeeAmountUSD,
        pathsToUSD
      );

      require(
        safeTransferFrom(uTokens[i].tokenAddress, feeAddress, batchFeeToPay),
        'Batch fee transferFrom() failed'
      );
    }

    // Batch contract pays the requests using Erc20FeeProxy
    for (uint256 i = 0; i < requestDetails.length; i++) {
      RequestDetail calldata rD = requestDetails[i];
      paymentErc20Proxy.transferFromWithReferenceAndFee(
        rD.path[0],
        rD.recipient,
        rD.requestAmount,
        rD.paymentReference,
        rD.feeAmount,
        feeAddress
      );
    }
    return batchFeeAmountUSD;
  }

  /*
   * Helper functions
   */

  /**
   * Top up the contract with enough `requestedToken` to pay `amountAndFee`.
   * The contract is NOT topped-up for `batchFeeAmount`.
   *
   * It also performs a few checks:
   * - checks that the batch contract has enough allowance from the payer
   * - checks that the payer has enough funds, including batch fees
   * - increases the allowance of the contract to use the payment proxy if needed
   *
   * @param requestedToken The token to pay
   * @param amountAndFee The amount and the fee for a token to pay
   * @param batchFeeAmount The batch fee amount for a token to pay
   * @param paymentProxyAddress The payment proxy address used to pay
   */
  function transferToContract(
    IERC20 requestedToken,
    uint256 amountAndFee,
    uint256 batchFeeAmount,
    address paymentProxyAddress
  ) internal {
    // Check proxy's allowance from user
    require(
      requestedToken.allowance(msg.sender, address(this)) >= amountAndFee,
      'Insufficient allowance for batch to pay'
    );
    // Check user's funds to pay amounts, it is an approximation for conversion payment
    require(
      requestedToken.balanceOf(msg.sender) >= amountAndFee + batchFeeAmount,
      'Not enough funds, including fees'
    );

    // Transfer the amount and fees (no batch fees) required for the token on the batch contract
    require(
      safeTransferFrom(address(requestedToken), address(this), amountAndFee),
      'payment transferFrom() failed'
    );

    // Batch contract approves Erc20ConversionProxy to spend the token
    if (requestedToken.allowance(address(this), paymentProxyAddress) < amountAndFee) {
      approvePaymentProxyToSpend(address(requestedToken), paymentProxyAddress);
    }
  }

  /**
   * It create a list of unique tokens used and the amounts associated.
   * It only considers tokens having: requestAmount + feeAmount > 0.
   * Regarding ERC20 no conversion payments:
   *   batchFeeAmount is the sum of requestAmount and feeAmount.
   *   Out of the function, batch fee rate is applied
   * @param requestDetails List of requests to pay.
   */
  function getUTokens(RequestDetail[] calldata requestDetails)
    internal
    pure
    returns (Token[] memory uTokens)
  {
    // A list of unique tokens, with the sum of maxToSpend by token
    uTokens = new Token[](requestDetails.length);
    for (uint256 i = 0; i < requestDetails.length; i++) {
      for (uint256 k = 0; k < requestDetails.length; k++) {
        RequestDetail calldata rD = requestDetails[i];
        // If the token is already in the existing uTokens list
        if (uTokens[k].tokenAddress == rD.path[rD.path.length - 1]) {
          if (rD.path.length > 1) {
            uTokens[k].amountAndFee += rD.maxToSpend;
          } else {
            // It is not a conversion payment
            uTokens[k].amountAndFee += rD.requestAmount + rD.feeAmount;
            uTokens[k].batchFeeAmount += rD.requestAmount;
          }
          break;
        }
        // If the token is not in the list (amountAndFee = 0)
        else if (
          uTokens[k].amountAndFee == 0 && (rD.maxToSpend > 0 || rD.requestAmount + rD.feeAmount > 0)
        ) {
          uTokens[k].tokenAddress = rD.path[rD.path.length - 1];

          if (rD.path.length > 1) {
            // amountAndFee is used to store _maxToSpend, useful to send enough tokens to this contract
            uTokens[k].amountAndFee = rD.maxToSpend;
          } else {
            // It is not a conversion payment
            uTokens[k].amountAndFee = rD.requestAmount + rD.feeAmount;
            uTokens[k].batchFeeAmount = rD.requestAmount;
          }
          break;
        }
      }
    }
  }

  /**
   * Calculate the batch fee amount to pay, using the USD fee limitation.
   * Without pathsToUSD or a wrong one, the fee limitation is not applied.
   * @param batchFeeToPay The amount of batch fee to pay
   * @param tokenAddress The address of the token
   * @param batchFeeAmountUSD The batch fee amount in USD already paid.
   * @param pathsToUSD The list of paths into USD for every token, used to limit the batch fees.
   *                   Without paths, there is not a fee limitation, and it consumes less gas.
   */
  function calculateBatchFeeToPay(
    uint256 batchFeeToPay,
    address tokenAddress,
    uint256 batchFeeAmountUSD,
    address[][] memory pathsToUSD
  ) internal view returns (uint256, uint256) {
    // Fees are not limited if there is no pathsToUSD
    // Excepted if batchFeeAmountUSD is already >= batchFeeAmountUSDLimit
    if (pathsToUSD.length == 0 && batchFeeAmountUSD < batchFeeAmountUSDLimit) {
      return (batchFeeToPay, batchFeeAmountUSD);
    }

    // Apply the fee limit and calculate if needed batchFeeToPay
    if (batchFeeAmountUSD < batchFeeAmountUSDLimit) {
      for (uint256 i = 0; i < pathsToUSD.length; i++) {
        // Check if the pathToUSD is right
        if (
          pathsToUSD[i][0] == tokenAddress && pathsToUSD[i][pathsToUSD[i].length - 1] == USDAddress
        ) {
          (uint256 conversionUSD, ) = chainlinkConversionPath.getConversion(
            batchFeeToPay,
            pathsToUSD[i]
          );
          // Calculate the batch fee to pay, taking care of the batchFeeAmountUSDLimit
          uint256 conversionToPayUSD = conversionUSD;
          if (batchFeeAmountUSD + conversionToPayUSD > batchFeeAmountUSDLimit) {
            conversionToPayUSD = batchFeeAmountUSDLimit - batchFeeAmountUSD;
            batchFeeToPay = (batchFeeToPay * conversionToPayUSD) / conversionUSD;
          }
          batchFeeAmountUSD += conversionToPayUSD;
          // Add only once the fees
          break;
        }
      }
    } else {
      batchFeeToPay = 0;
    }
    return (batchFeeToPay, batchFeeAmountUSD);
  }

  /**
   * @notice Authorizes the proxy to spend a new request currency (ERC20).
   * @param _erc20Address Address of an ERC20 used as the request currency.
   * @param _paymentErc20Proxy Address of the proxy.
   */
  function approvePaymentProxyToSpend(address _erc20Address, address _paymentErc20Proxy) internal {
    IERC20 erc20 = IERC20(_erc20Address);
    uint256 max = 2**256 - 1;
    erc20.safeApprove(address(_paymentErc20Proxy), max);
  }

  /**
   * @notice Call transferFrom ERC20 function and validates the return data of a ERC20 contract call.
   * @dev This is necessary because of non-standard ERC20 tokens that don't have a return value.
   * @return result The return value of the ERC20 call, returning true for non-standard tokens
   */
  function safeTransferFrom(
    address _tokenAddress,
    address _to,
    uint256 _amount
  ) internal returns (bool result) {
    /* solium-disable security/no-inline-assembly */
    // check if the address is a contract
    assembly {
      if iszero(extcodesize(_tokenAddress)) {
        revert(0, 0)
      }
    }

    // solium-disable-next-line security/no-low-level-calls
    (bool success, ) = _tokenAddress.call(
      abi.encodeWithSignature('transferFrom(address,address,uint256)', msg.sender, _to, _amount)
    );

    assembly {
      switch returndatasize()
      case 0 {
        // Not a standard erc20
        result := 1
      }
      case 32 {
        // Standard erc20
        returndatacopy(0, 0, 32)
        result := mload(0)
      }
      default {
        // Anything else, should revert for safety
        revert(0, 0)
      }
    }

    require(success, 'transferFrom() has been reverted');

    /* solium-enable security/no-inline-assembly */
    return result;
  }

  /*
   * Admin functions to edit the proxies address and fees
   */

  /**
   * @notice Fees added when using Erc20/Native batch functions
   * @param _batchFee Between 0 and 200, i.e: batchFee = 30 represent 0.30% of fee
   */
  function setBatchFee(uint16 _batchFee) external onlyOwner {
    // safety to avoid wrong setting
    require(_batchFee <= 200, 'The batch fee value is too high: > 2%');
    batchFee = _batchFee;
  }

  /**
   * @param _paymentErc20Proxy The address to the Erc20 fee payment proxy to use.
   */
  function setPaymentErc20Proxy(address _paymentErc20Proxy) external onlyOwner {
    paymentErc20Proxy = IERC20FeeProxy(_paymentErc20Proxy);
  }

  /**
   * @param _paymentNativeProxy The address to the Native fee payment proxy to use.
   */
  function setPaymentNativeProxy(address _paymentNativeProxy) external onlyOwner {
    paymentNativeProxy = IEthereumFeeProxy(_paymentNativeProxy);
  }

  /**
   * @notice Update the conversion path contract used to fetch conversions.
   * @param _chainlinkConversionPath The address of the conversion path contract.
   */
  function setChainlinkConversionPath(address _chainlinkConversionPath) external onlyOwner {
    chainlinkConversionPath = ChainlinkConversionPath(_chainlinkConversionPath);
  }

  /**
   * This function define variables allowing to limit the fees:
   * NativeAddress, USDAddress, and pathsNativeToUSD.
   * @param _NativeAddress The address representing the Native currency.
   * @param _USDAddress The address representing the USD currency.
   */
  function setNativeAndUSDAddress(address _NativeAddress, address _USDAddress) external onlyOwner {
    NativeAddress = _NativeAddress;
    USDAddress = _USDAddress;
    pathsNativeToUSD = [[NativeAddress, USDAddress]];
  }

  /**
   * @param _batchFeeAmountUSDLimit The limitation of the batch fee amount in USD, e.g:
   *                                batchFeeAmountUSDLimit = 150 * 1e8 represents $150
   */
  function setBatchFeeAmountUSDLimit(uint64 _batchFeeAmountUSDLimit) external onlyOwner {
    batchFeeAmountUSDLimit = _batchFeeAmountUSDLimit;
  }
}

File 5 of 15 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

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

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

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

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

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

    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

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

File 6 of 15 : SafeERC20.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import '@openzeppelin/contracts/token/ERC20/ERC20.sol';

/**
 * @title SafeERC20
 * @notice Works around implementations of ERC20 with transferFrom not returning success status.
 */
library SafeERC20 {
  /**
   * @notice Call transferFrom ERC20 function and validates the return data of a ERC20 contract call.
   * @dev This is necessary because of non-standard ERC20 tokens that don't have a return value.
   * @return result The return value of the ERC20 call, returning true for non-standard tokens
   */
  function safeTransferFrom(
    IERC20 _token,
    address _from,
    address _to,
    uint256 _amount
  ) internal returns (bool result) {
    // solium-disable-next-line security/no-low-level-calls
    (bool success, bytes memory data) = address(_token).call(
      abi.encodeWithSignature('transferFrom(address,address,uint256)', _from, _to, _amount)
    );

    return success && (data.length == 0 || abi.decode(data, (bool)));
  }

  /**
   * @notice Call approve ERC20 function and validates the return data of a ERC20 contract call.
   * @dev This is necessary because of non-standard ERC20 tokens that don't have a return value.
   * @return result The return value of the ERC20 call, returning true for non-standard tokens
   */
  function safeApprove(
    IERC20 _token,
    address _spender,
    uint256 _amount
  ) internal returns (bool result) {
    // solium-disable-next-line security/no-low-level-calls
    (bool success, bytes memory data) = address(_token).call(
      abi.encodeWithSignature('approve(address,uint256)', _spender, _amount)
    );

    return success && (data.length == 0 || abi.decode(data, (bool)));
  }

  /**
   * @notice Call transfer ERC20 function and validates the return data of a ERC20 contract call.
   * @dev This is necessary because of non-standard ERC20 tokens that don't have a return value.
   * @return result The return value of the ERC20 call, returning true for non-standard tokens
   */
  function safeTransfer(
    IERC20 _token,
    address _to,
    uint256 _amount
  ) internal returns (bool result) {
    // solium-disable-next-line security/no-low-level-calls
    (bool success, bytes memory data) = address(_token).call(
      abi.encodeWithSignature('transfer(address,uint256)', _to, _amount)
    );

    return success && (data.length == 0 || abi.decode(data, (bool)));
  }
}

File 7 of 15 : Ownable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)

pragma solidity ^0.8.0;

import "../utils/Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {
        _transferOwnership(_msgSender());
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
        _;
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

File 8 of 15 : ERC20FeeProxy.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IERC20FeeProxy {
  event TransferWithReferenceAndFee(
    address tokenAddress,
    address to,
    uint256 amount,
    bytes indexed paymentReference,
    uint256 feeAmount,
    address feeAddress
  );

  function transferFromWithReferenceAndFee(
    address _tokenAddress,
    address _to,
    uint256 _amount,
    bytes calldata _paymentReference,
    uint256 _feeAmount,
    address _feeAddress
  ) external;
}

File 9 of 15 : EthereumFeeProxy.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IEthereumFeeProxy {
  event TransferWithReferenceAndFee(
    address to,
    uint256 amount,
    bytes indexed paymentReference,
    uint256 feeAmount,
    address feeAddress
  );

  function transferWithReferenceAndFee(
    address payable _to,
    bytes calldata _paymentReference,
    uint256 _feeAmount,
    address payable _feeAddress
  ) external payable;
}

File 10 of 15 : ChainlinkConversionPath.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import './legacy_openzeppelin/contracts/access/roles/WhitelistAdminRole.sol';

interface ERC20fraction {
  function decimals() external view returns (uint8);
}

interface AggregatorFraction {
  function decimals() external view returns (uint8);

  function latestAnswer() external view returns (int256);

  function latestTimestamp() external view returns (uint256);
}

/**
 * @title ChainlinkConversionPath
 *
 * @notice ChainlinkConversionPath is a contract computing currency conversion rates based on Chainlink aggretators
 */
contract ChainlinkConversionPath is WhitelistAdminRole {
  uint256 constant PRECISION = 1e18;
  uint256 constant NATIVE_TOKEN_DECIMALS = 18;
  uint256 constant FIAT_DECIMALS = 8;
  address public nativeTokenHash;

  /**
   * @param _nativeTokenHash hash of the native token
   */
  constructor(address _nativeTokenHash) {
    nativeTokenHash = _nativeTokenHash;
  }

  // Mapping of Chainlink aggregators (input currency => output currency => contract address)
  // input & output currencies are the addresses of the ERC20 contracts OR the sha3("currency code")
  mapping(address => mapping(address => address)) public allAggregators;

  // declare a new aggregator
  event AggregatorUpdated(address _input, address _output, address _aggregator);

  /**
   * @notice Update an aggregator
   * @param _input address representing the input currency
   * @param _output address representing the output currency
   * @param _aggregator address of the aggregator contract
   */
  function updateAggregator(
    address _input,
    address _output,
    address _aggregator
  ) external onlyWhitelistAdmin {
    allAggregators[_input][_output] = _aggregator;
    emit AggregatorUpdated(_input, _output, _aggregator);
  }

  /**
   * @notice Update a list of aggregators
   * @param _inputs list of addresses representing the input currencies
   * @param _outputs list of addresses representing the output currencies
   * @param _aggregators list of addresses of the aggregator contracts
   */
  function updateAggregatorsList(
    address[] calldata _inputs,
    address[] calldata _outputs,
    address[] calldata _aggregators
  ) external onlyWhitelistAdmin {
    require(_inputs.length == _outputs.length, 'arrays must have the same length');
    require(_inputs.length == _aggregators.length, 'arrays must have the same length');

    // For every conversions of the path
    for (uint256 i; i < _inputs.length; i++) {
      allAggregators[_inputs[i]][_outputs[i]] = _aggregators[i];
      emit AggregatorUpdated(_inputs[i], _outputs[i], _aggregators[i]);
    }
  }

  /**
   * @notice Computes the conversion of an amount through a list of intermediate conversions
   * @param _amountIn Amount to convert
   * @param _path List of addresses representing the currencies for the intermediate conversions
   * @return result The result after all the conversions
   * @return oldestRateTimestamp The oldest timestamp of the path
   */
  function getConversion(uint256 _amountIn, address[] calldata _path)
    external
    view
    returns (uint256 result, uint256 oldestRateTimestamp)
  {
    (uint256 rate, uint256 timestamp, uint256 decimals) = getRate(_path);

    // initialize the result
    result = (_amountIn * rate) / decimals;

    oldestRateTimestamp = timestamp;
  }

  /**
   * @notice Computes the conversion rate from a list of currencies
   * @param _path List of addresses representing the currencies for the conversions
   * @return rate The rate
   * @return oldestRateTimestamp The oldest timestamp of the path
   * @return decimals of the conversion rate
   */
  function getRate(address[] memory _path)
    public
    view
    returns (
      uint256 rate,
      uint256 oldestRateTimestamp,
      uint256 decimals
    )
  {
    // initialize the result with 18 decimals (for more precision)
    rate = PRECISION;
    decimals = PRECISION;
    oldestRateTimestamp = block.timestamp;

    // For every conversion of the path
    for (uint256 i; i < _path.length - 1; i++) {
      (
        AggregatorFraction aggregator,
        bool reverseAggregator,
        uint256 decimalsInput,
        uint256 decimalsOutput
      ) = getAggregatorAndDecimals(_path[i], _path[i + 1]);

      // store the latest timestamp of the path
      uint256 currentTimestamp = aggregator.latestTimestamp();
      if (currentTimestamp < oldestRateTimestamp) {
        oldestRateTimestamp = currentTimestamp;
      }

      // get the rate of the current step
      uint256 currentRate = uint256(aggregator.latestAnswer());
      // get the number of decimals of the current rate
      uint256 decimalsAggregator = uint256(aggregator.decimals());

      // mul with the difference of decimals before the current rate computation (for more precision)
      if (decimalsAggregator > decimalsInput) {
        rate = rate * (10**(decimalsAggregator - decimalsInput));
      }
      if (decimalsAggregator < decimalsOutput) {
        rate = rate * (10**(decimalsOutput - decimalsAggregator));
      }

      // Apply the current rate (if path uses an aggregator in the reverse way, div instead of mul)
      if (reverseAggregator) {
        rate = (rate * (10**decimalsAggregator)) / currentRate;
      } else {
        rate = (rate * currentRate) / (10**decimalsAggregator);
      }

      // div with the difference of decimals AFTER the current rate computation (for more precision)
      if (decimalsAggregator < decimalsInput) {
        rate = rate / (10**(decimalsInput - decimalsAggregator));
      }
      if (decimalsAggregator > decimalsOutput) {
        rate = rate / (10**(decimalsAggregator - decimalsOutput));
      }
    }
  }

  /**
   * @notice Gets aggregators and decimals of two currencies
   * @param _input input Address
   * @param _output output Address
   * @return aggregator to get the rate between the two currencies
   * @return reverseAggregator true if the aggregator returned give the rate from _output to _input
   * @return decimalsInput decimals of _input
   * @return decimalsOutput decimals of _output
   */
  function getAggregatorAndDecimals(address _input, address _output)
    private
    view
    returns (
      AggregatorFraction aggregator,
      bool reverseAggregator,
      uint256 decimalsInput,
      uint256 decimalsOutput
    )
  {
    // Try to get the right aggregator for the conversion
    aggregator = AggregatorFraction(allAggregators[_input][_output]);
    reverseAggregator = false;

    // if no aggregator found we try to find an aggregator in the reverse way
    if (address(aggregator) == address(0x00)) {
      aggregator = AggregatorFraction(allAggregators[_output][_input]);
      reverseAggregator = true;
    }

    require(address(aggregator) != address(0x00), 'No aggregator found');

    // get the decimals for the two currencies
    decimalsInput = getDecimals(_input);
    decimalsOutput = getDecimals(_output);
  }

  /**
   * @notice Gets decimals from an address currency
   * @param _addr address to check
   * @return decimals number of decimals
   */
  function getDecimals(address _addr) private view returns (uint256 decimals) {
    // by default we assume it is fiat
    decimals = FIAT_DECIMALS;
    // if address is the hash of the ETH currency
    if (_addr == nativeTokenHash) {
      decimals = NATIVE_TOKEN_DECIMALS;
    } else if (isContract(_addr)) {
      // otherwise, we get the decimals from the erc20 directly
      decimals = ERC20fraction(_addr).decimals();
    }
  }

  /**
   * @notice Checks if an address is a contract
   * @param _addr Address to check
   * @return true if the address hosts a contract, false otherwise
   */
  function isContract(address _addr) private view returns (bool) {
    uint32 size;
    // solium-disable security/no-inline-assembly
    assembly {
      size := extcodesize(_addr)
    }
    return (size > 0);
  }
}

File 11 of 15 : ERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/ERC20.sol)

pragma solidity ^0.8.0;

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

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

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

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;

    /**
     * @dev Sets the values for {name} and {symbol}.
     *
     * The default value of {decimals} is 18. To select a different value for
     * {decimals} you should overload it.
     *
     * All two of these values are immutable: they can only be set once during
     * construction.
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

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

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

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

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

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

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

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

    /**
     * @dev See {IERC20-approve}.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 amount) public virtual override returns (bool) {
        _approve(_msgSender(), spender, amount);
        return true;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Emits an {Approval} event indicating the updated allowance. This is not
     * required by the EIP. See the note at the beginning of {ERC20}.
     *
     * Requirements:
     *
     * - `sender` and `recipient` cannot be the zero address.
     * - `sender` must have a balance of at least `amount`.
     * - the caller must have allowance for ``sender``'s tokens of at least
     * `amount`.
     */
    function transferFrom(
        address sender,
        address recipient,
        uint256 amount
    ) public virtual override returns (bool) {
        _transfer(sender, recipient, amount);

        uint256 currentAllowance = _allowances[sender][_msgSender()];
        require(currentAllowance >= amount, "ERC20: transfer amount exceeds allowance");
        unchecked {
            _approve(sender, _msgSender(), currentAllowance - amount);
        }

        return true;
    }

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

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

        return true;
    }

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

        _beforeTokenTransfer(sender, recipient, amount);

        uint256 senderBalance = _balances[sender];
        require(senderBalance >= amount, "ERC20: transfer amount exceeds balance");
        unchecked {
            _balances[sender] = senderBalance - amount;
        }
        _balances[recipient] += amount;

        emit Transfer(sender, recipient, amount);

        _afterTokenTransfer(sender, recipient, amount);
    }

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

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

        _totalSupply += amount;
        _balances[account] += amount;
        emit Transfer(address(0), account, amount);

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

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

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

        uint256 accountBalance = _balances[account];
        require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
        unchecked {
            _balances[account] = accountBalance - amount;
        }
        _totalSupply -= amount;

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

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

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

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

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

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

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

pragma solidity ^0.8.0;

import "../IERC20.sol";

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

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

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

File 13 of 15 : Context.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;

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

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

File 14 of 15 : WhitelistAdminRole.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import '@openzeppelin/contracts/utils/Context.sol';
import '../Roles.sol';

/**
 * @title WhitelistAdminRole
 * @dev WhitelistAdmins are responsible for assigning and removing Whitelisted accounts.
 */
abstract contract WhitelistAdminRole is Context {
  using Roles for Roles.Role;

  event WhitelistAdminAdded(address indexed account);
  event WhitelistAdminRemoved(address indexed account);

  Roles.Role private _whitelistAdmins;

  constructor() {
    _addWhitelistAdmin(_msgSender());
  }

  modifier onlyWhitelistAdmin() {
    require(
      isWhitelistAdmin(_msgSender()),
      'WhitelistAdminRole: caller does not have the WhitelistAdmin role'
    );
    _;
  }

  function isWhitelistAdmin(address account) public view returns (bool) {
    return _whitelistAdmins.has(account);
  }

  function addWhitelistAdmin(address account) public onlyWhitelistAdmin {
    _addWhitelistAdmin(account);
  }

  function renounceWhitelistAdmin() public {
    _removeWhitelistAdmin(_msgSender());
  }

  function _addWhitelistAdmin(address account) internal {
    _whitelistAdmins.add(account);
    emit WhitelistAdminAdded(account);
  }

  function _removeWhitelistAdmin(address account) internal {
    _whitelistAdmins.remove(account);
    emit WhitelistAdminRemoved(account);
  }
}

File 15 of 15 : Roles.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/**
 * @title Roles
 * @dev Library for managing addresses assigned to a Role.
 */
library Roles {
  struct Role {
    mapping(address => bool) bearer;
  }

  /**
   * @dev Give an account access to this role.
   */
  function add(Role storage role, address account) internal {
    require(!has(role, account), 'Roles: account already has role');
    role.bearer[account] = true;
  }

  /**
   * @dev Remove an account's access to this role.
   */
  function remove(Role storage role, address account) internal {
    require(has(role, account), 'Roles: account does not have role');
    role.bearer[account] = false;
  }

  /**
   * @dev Check if an account has this role.
   * @return bool
   */
  function has(Role storage role, address account) internal view returns (bool) {
    require(account != address(0), 'Roles: account is the zero address');
    return role.bearer[account];
  }
}

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

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"_paymentErc20Proxy","type":"address"},{"internalType":"address","name":"_paymentNativeProxy","type":"address"},{"internalType":"address","name":"_paymentErc20ConversionProxy","type":"address"},{"internalType":"address","name":"_paymentNativeConversionFeeProxy","type":"address"},{"internalType":"address","name":"_chainlinkConversionPath","type":"address"},{"internalType":"address","name":"_owner","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[],"name":"NativeAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"USDAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"requestAmount","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"bytes","name":"paymentReference","type":"bytes"},{"internalType":"uint256","name":"feeAmount","type":"uint256"},{"internalType":"uint256","name":"maxToSpend","type":"uint256"},{"internalType":"uint256","name":"maxRateTimespan","type":"uint256"}],"internalType":"struct BatchNoConversionPayments.RequestDetail[]","name":"requestDetails","type":"tuple[]"},{"internalType":"address[][]","name":"pathsToUSD","type":"address[][]"},{"internalType":"address","name":"feeAddress","type":"address"}],"name":"batchERC20Payments","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"batchFee","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"batchFeeAmountUSDLimit","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"requestAmount","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"bytes","name":"paymentReference","type":"bytes"},{"internalType":"uint256","name":"feeAmount","type":"uint256"},{"internalType":"uint256","name":"maxToSpend","type":"uint256"},{"internalType":"uint256","name":"maxRateTimespan","type":"uint256"}],"internalType":"struct BatchNoConversionPayments.RequestDetail[]","name":"requestDetails","type":"tuple[]"},{"internalType":"address[][]","name":"pathsToUSD","type":"address[][]"},{"internalType":"address","name":"feeAddress","type":"address"}],"name":"batchMultiERC20ConversionPayments","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"requestAmount","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"bytes","name":"paymentReference","type":"bytes"},{"internalType":"uint256","name":"feeAmount","type":"uint256"},{"internalType":"uint256","name":"maxToSpend","type":"uint256"},{"internalType":"uint256","name":"maxRateTimespan","type":"uint256"}],"internalType":"struct BatchNoConversionPayments.RequestDetail[]","name":"requestDetails","type":"tuple[]"},{"internalType":"address[][]","name":"pathsToUSD","type":"address[][]"},{"internalType":"address","name":"feeAddress","type":"address"}],"name":"batchMultiERC20Payments","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"requestAmount","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"bytes","name":"paymentReference","type":"bytes"},{"internalType":"uint256","name":"feeAmount","type":"uint256"},{"internalType":"uint256","name":"maxToSpend","type":"uint256"},{"internalType":"uint256","name":"maxRateTimespan","type":"uint256"}],"internalType":"struct BatchNoConversionPayments.RequestDetail[]","name":"requestDetails","type":"tuple[]"},{"internalType":"bool","name":"skipFeeUSDLimit","type":"bool"},{"internalType":"address payable","name":"feeAddress","type":"address"}],"name":"batchNativeConversionPayments","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"requestAmount","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"bytes","name":"paymentReference","type":"bytes"},{"internalType":"uint256","name":"feeAmount","type":"uint256"},{"internalType":"uint256","name":"maxToSpend","type":"uint256"},{"internalType":"uint256","name":"maxRateTimespan","type":"uint256"}],"internalType":"struct BatchNoConversionPayments.RequestDetail[]","name":"requestDetails","type":"tuple[]"},{"internalType":"bool","name":"skipFeeUSDLimit","type":"bool"},{"internalType":"address payable","name":"feeAddress","type":"address"}],"name":"batchNativePayments","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"paymentNetworkId","type":"uint256"},{"components":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"requestAmount","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"bytes","name":"paymentReference","type":"bytes"},{"internalType":"uint256","name":"feeAmount","type":"uint256"},{"internalType":"uint256","name":"maxToSpend","type":"uint256"},{"internalType":"uint256","name":"maxRateTimespan","type":"uint256"}],"internalType":"struct BatchNoConversionPayments.RequestDetail[]","name":"requestDetails","type":"tuple[]"}],"internalType":"struct BatchConversionPayments.MetaDetail[]","name":"metaDetails","type":"tuple[]"},{"internalType":"address[][]","name":"pathsToUSD","type":"address[][]"},{"internalType":"address","name":"feeAddress","type":"address"}],"name":"batchPayments","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"chainlinkConversionPath","outputs":[{"internalType":"contract ChainlinkConversionPath","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"pathsNativeToUSD","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"paymentErc20ConversionProxy","outputs":[{"internalType":"contract IERC20ConversionProxy","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"paymentErc20Proxy","outputs":[{"internalType":"contract IERC20FeeProxy","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"paymentNativeConversionProxy","outputs":[{"internalType":"contract IEthConversionProxy","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"paymentNativeProxy","outputs":[{"internalType":"contract IEthereumFeeProxy","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"_batchFee","type":"uint16"}],"name":"setBatchFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"_batchFeeAmountUSDLimit","type":"uint64"}],"name":"setBatchFeeAmountUSDLimit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_chainlinkConversionPath","type":"address"}],"name":"setChainlinkConversionPath","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_NativeAddress","type":"address"},{"internalType":"address","name":"_USDAddress","type":"address"}],"name":"setNativeAndUSDAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_paymentErc20ConversionProxy","type":"address"}],"name":"setPaymentErc20ConversionProxy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_paymentErc20Proxy","type":"address"}],"name":"setPaymentErc20Proxy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_paymentNativeConversionProxy","type":"address"}],"name":"setPaymentNativeConversionProxy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_paymentNativeProxy","type":"address"}],"name":"setPaymentNativeProxy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]

6080604052612710600360166101000a81548161ffff021916908361ffff1602179055506001600460006101000a81548160ff0219169083151502179055506000600860146101000a81548160ff0219169083151502179055503480156200006657600080fd5b5060405162005a2e38038062005a2e83398181016040528101906200008c9190620004a9565b85858383620000b0620000a46200023460201b60201c565b6200023c60201b60201c565b83600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555082600260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555081600360006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555062000184816200030060201b60201c565b6000600360146101000a81548161ffff021916908361ffff1602179055505050505083600760006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555082600860006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050505050505062000660565b600033905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050816000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b620003106200023460201b60201c565b73ffffffffffffffffffffffffffffffffffffffff16620003366200041660201b60201c565b73ffffffffffffffffffffffffffffffffffffffff16146200038f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016200038690620005a6565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141562000402576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401620003f9906200063e565b60405180910390fd5b62000413816200023c60201b60201c565b50565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000620004718262000444565b9050919050565b620004838162000464565b81146200048f57600080fd5b50565b600081519050620004a38162000478565b92915050565b60008060008060008060c08789031215620004c957620004c86200043f565b5b6000620004d989828a0162000492565b9650506020620004ec89828a0162000492565b9550506040620004ff89828a0162000492565b94505060606200051289828a0162000492565b93505060806200052589828a0162000492565b92505060a06200053889828a0162000492565b9150509295509295509295565b600082825260208201905092915050565b7f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572600082015250565b60006200058e60208362000545565b91506200059b8262000556565b602082019050919050565b60006020820190508181036000830152620005c1816200057f565b9050919050565b7f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160008201527f6464726573730000000000000000000000000000000000000000000000000000602082015250565b60006200062660268362000545565b91506200063382620005c8565b604082019050919050565b60006020820190508181036000830152620006598162000617565b9050919050565b6153be80620006706000396000f3fe6080604052600436106101a05760003560e01c80638da2f6d8116100ec578063b677bf0e1161008a578063e695f75c11610064578063e695f75c14610606578063f2fde38b1461062f578063f4d0841214610658578063f8b823e41461068157610201565b8063b677bf0e14610575578063dae3d6bb146105b2578063df6557ec146105dd57610201565b8063946647f1116100c6578063946647f1146104a557806397aa096e146104d05780639af8a581146104fb578063a4b519ff1461053857610201565b80638da2f6d8146104355780638da5cb5b1461045e57806392cddb911461048957610201565b80633085df631161015957806360be74e61161013357806360be74e6146103a1578063715018a6146103cc5780637abff543146103e357806381fe66ce1461040c57610201565b80633085df631461030b578063333ec062146103485780634465549a1461037857610201565b80630917377f146102065780630a9157c4146102315780630e48b8db1461025c57806316ac30a91461028c5780632e2f0ca0146102b55780632fae95bf146102e057610201565b3661020157600860149054906101000a900460ff16806101c05750600034145b6101ff576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016101f690613a00565b60405180910390fd5b005b600080fd5b34801561021257600080fd5b5061021b6106ac565b6040516102289190613a61565b60405180910390f35b34801561023d57600080fd5b506102466106d2565b6040516102539190613adb565b60405180910390f35b61027660048036038101906102719190613be5565b6106f8565b6040516102839190613c72565b60405180910390f35b34801561029857600080fd5b506102b360048036038101906102ae9190613ccd565b610712565b005b3480156102c157600080fd5b506102ca6107ba565b6040516102d79190613d1b565b60405180910390f35b3480156102ec57600080fd5b506102f56107e0565b6040516103029190613d45565b60405180910390f35b34801561031757600080fd5b50610332600480360381019061032d9190613d8c565b6107fa565b60405161033f9190613a61565b60405180910390f35b610362600480360381019061035d9190613be5565b610854565b60405161036f9190613c72565b60405180910390f35b34801561038457600080fd5b5061039f600480360381019061039a9190613df8565b61086e565b005b3480156103ad57600080fd5b506103b661092e565b6040516103c39190613e46565b60405180910390f35b3480156103d857600080fd5b506103e1610954565b005b3480156103ef57600080fd5b5061040a60048036038101906104059190613df8565b6109dc565b005b34801561041857600080fd5b50610433600480360381019061042e9190613df8565b610a9c565b005b34801561044157600080fd5b5061045c60048036038101906104579190613df8565b610b5c565b005b34801561046a57600080fd5b50610473610c1c565b6040516104809190613a61565b60405180910390f35b6104a3600480360381019061049e9190613f0d565b610c45565b005b3480156104b157600080fd5b506104ba610f07565b6040516104c79190613fc3565b60405180910390f35b3480156104dc57600080fd5b506104e5610f2d565b6040516104f29190613a61565b60405180910390f35b34801561050757600080fd5b50610522600480360381019061051d9190613fde565b610f53565b60405161052f9190613c72565b60405180910390f35b34801561054457600080fd5b5061055f600480360381019061055a9190613fde565b610f6f565b60405161056c9190613c72565b60405180910390f35b34801561058157600080fd5b5061059c60048036038101906105979190613fde565b610f8b565b6040516105a99190613c72565b60405180910390f35b3480156105be57600080fd5b506105c7610fa7565b6040516105d49190614094565b60405180910390f35b3480156105e957600080fd5b5061060460048036038101906105ff91906140af565b610fcd565b005b34801561061257600080fd5b5061062d60048036038101906106289190613df8565b61119f565b005b34801561063b57600080fd5b5061065660048036038101906106519190613df8565b61125f565b005b34801561066457600080fd5b5061067f600480360381019061067a9190614129565b611357565b005b34801561068d57600080fd5b5061069661143b565b6040516106a39190614165565b60405180910390f35b600460019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600061070885858560008661144f565b9050949350505050565b61071a61188c565b73ffffffffffffffffffffffffffffffffffffffff16610738610c1c565b73ffffffffffffffffffffffffffffffffffffffff161461078e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610785906141cc565b60405180910390fd5b80600360186101000a81548167ffffffffffffffff021916908367ffffffffffffffff16021790555050565b600760009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600360189054906101000a900467ffffffffffffffff1681565b6006828154811061080a57600080fd5b90600052602060002001818154811061082257600080fd5b906000526020600020016000915091509054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000610864858585600086611894565b9050949350505050565b61087661188c565b73ffffffffffffffffffffffffffffffffffffffff16610894610c1c565b73ffffffffffffffffffffffffffffffffffffffff16146108ea576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108e1906141cc565b60405180910390fd5b80600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b600860009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b61095c61188c565b73ffffffffffffffffffffffffffffffffffffffff1661097a610c1c565b73ffffffffffffffffffffffffffffffffffffffff16146109d0576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016109c7906141cc565b60405180910390fd5b6109da6000611d0d565b565b6109e461188c565b73ffffffffffffffffffffffffffffffffffffffff16610a02610c1c565b73ffffffffffffffffffffffffffffffffffffffff1614610a58576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610a4f906141cc565b60405180910390fd5b80600860006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b610aa461188c565b73ffffffffffffffffffffffffffffffffffffffff16610ac2610c1c565b73ffffffffffffffffffffffffffffffffffffffff1614610b18576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b0f906141cc565b60405180910390fd5b80600760006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b610b6461188c565b73ffffffffffffffffffffffffffffffffffffffff16610b82610c1c565b73ffffffffffffffffffffffffffffffffffffffff1614610bd8576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610bcf906141cc565b60405180910390fd5b80600260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60068585905010610c8b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610c8290614238565b60405180910390fd5b6000805b86869050811015610efe5736878783818110610cae57610cad614258565b5b9050602002810190610cc09190614296565b9050600081600001351415610cfd57610ceb818060200190610ce291906142be565b85898989611dd1565b83610cf69190614350565b9250610eea565b600181600001351415610d3857610d26818060200190610d1d91906142be565b888887896122b3565b83610d319190614350565b9250610ee9565b600281600001351415610d7357610d61818060200190610d5891906142be565b888887896126bd565b83610d6c9190614350565b9250610ee8565b600381600001351415610e6b576004888860018b8b9050610d9491906143a6565b818110610da457610da3614258565b5b9050602002810190610db69190614296565b600001351415610ddc576000600460006101000a81548160ff0219169083151502179055505b610dfd818060200190610def91906142be565b600089899050148688611894565b83610e089190614350565b92506004888860018b8b9050610e1e91906143a6565b818110610e2e57610e2d614258565b5b9050602002810190610e409190614296565b600001351415610e66576001600460006101000a81548160ff0219169083151502179055505b610ee7565b600481600001351415610eab57610e99818060200190610e8b91906142be565b60008989905014868861144f565b83610ea49190614350565b9250610ee6565b6040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610edd90614426565b60405180910390fd5b5b5b5b5b508080610ef690614446565b915050610c8f565b50505050505050565b600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000610f64868686866000876122b3565b905095945050505050565b6000610f80868686866000876126bd565b905095945050505050565b6000610f9c86866000878787611dd1565b905095945050505050565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b610fd561188c565b73ffffffffffffffffffffffffffffffffffffffff16610ff3610c1c565b73ffffffffffffffffffffffffffffffffffffffff1614611049576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611040906141cc565b60405180910390fd5b81600560006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555080600460016101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060405180602001604052806040518060400160405280600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001600460019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815250815250600690600161119a929190613825565b505050565b6111a761188c565b73ffffffffffffffffffffffffffffffffffffffff166111c5610c1c565b73ffffffffffffffffffffffffffffffffffffffff161461121b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611212906141cc565b60405180910390fd5b80600360006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b61126761188c565b73ffffffffffffffffffffffffffffffffffffffff16611285610c1c565b73ffffffffffffffffffffffffffffffffffffffff16146112db576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016112d2906141cc565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141561134b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161134290614501565b60405180910390fd5b61135481611d0d565b50565b61135f61188c565b73ffffffffffffffffffffffffffffffffffffffff1661137d610c1c565b73ffffffffffffffffffffffffffffffffffffffff16146113d3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016113ca906141cc565b60405180910390fd5b60c88161ffff16111561141b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161141290614593565b60405180910390fd5b80600360146101000a81548161ffff021916908361ffff16021790555050565b600360149054906101000a900461ffff1681565b6000804790506001600860146101000a81548160ff02191690831515021790555060005b87879050811015611592573688888381811061149257611491614258565b5b90506020028101906114a491906145b3565b9050600860009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663ac473c8a478360000160208101906114f89190613df8565b846020013585806040019061150d91906145db565b87806060019061151d919061463e565b89608001358e8b60c001356040518b63ffffffff1660e01b815260040161154c999897969594939291906147e3565b6000604051808303818588803b15801561156557600080fd5b505af1158015611579573d6000803e3d6000fd5b505050505050808061158a90614446565b915050611473565b506000600360169054906101000a900461ffff1661ffff16600360149054906101000a900461ffff1661ffff1647846115cb91906143a6565b6115d59190614864565b6115df91906148ed565b905060001515861515141561172c5761172381600660008154811061160757611606614258565b5b9060005260206000200160008154811061162457611623614258565b5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16876006805480602002602001604051908101604052809291908181526020016000905b8282101561171a5783829060005260206000200180548060200260200160405190810160405280929190818152602001828054801561170657602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190600101908083116116bc575b505050505081526020019060010190611672565b50505050612a3e565b80965081925050505b8047101561176f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161176690614990565b60405180910390fd5b8373ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f193505050501580156117b5573d6000803e3d6000fd5b5060003373ffffffffffffffffffffffffffffffffffffffff16476040516117dc906149e1565b60006040518083038185875af1925050503d8060008114611819576040519150601f19603f3d011682016040523d82523d6000602084013e61181e565b606091505b5050905080611862576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161185990614a68565b60405180910390fd5b6000600860146101000a81548160ff02191690831515021790555085935050505095945050505050565b600033905090565b6000806000905060005b87879050811015611a1a57368888838181106118bd576118bc614258565b5b90506020028101906118cf91906145b3565b9050806080013581602001356118e59190614350565b471015611927576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161191e90614ad4565b60405180910390fd5b8060200135836119379190614350565b9250600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663b868980b8260800135836020013561198b9190614350565b83600001602081019061199e9190613df8565b8480606001906119ae919061463e565b86608001358b6040518763ffffffff1660e01b81526004016119d4959493929190614b03565b6000604051808303818588803b1580156119ed57600080fd5b505af1158015611a01573d6000803e3d6000fd5b5050505050508080611a1290614446565b91505061189e565b50600360169054906101000a900461ffff1661ffff16600360149054906101000a900461ffff1661ffff1682611a509190614864565b611a5a91906148ed565b9050600015158515151415611ba757611b9e816006600081548110611a8257611a81614258565b5b90600052602060002001600081548110611a9f57611a9e614258565b5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16866006805480602002602001604051908101604052809291908181526020016000905b82821015611b9557838290600052602060002001805480602002602001604051908101604052809291908181526020018280548015611b8157602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019060010190808311611b37575b505050505081526020019060010190611aed565b50505050612a3e565b80955081925050505b80471015611bea576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611be190614b9d565b60405180910390fd5b8273ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f19350505050158015611c30573d6000803e3d6000fd5b50600460009054906101000a900460ff168015611c4d5750600047115b15611d005760003373ffffffffffffffffffffffffffffffffffffffff1647604051611c78906149e1565b60006040518083038185875af1925050503d8060008114611cb5576040519150601f19603f3d011682016040523d82523d6000602084013e611cba565b606091505b5050905080611cfe576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611cf590614a68565b60405180910390fd5b505b8391505095945050505050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050816000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b600080611dde8888612d60565b9050600080600090505b825181108015611e1657506000838281518110611e0857611e07614258565b5b602002602001015160200151115b15611f3957600360169054906101000a900461ffff1661ffff16600360149054906101000a900461ffff1661ffff16848381518110611e5857611e57614258565b5b602002602001015160200151611e6e9190614864565b611e7891906148ed565b838281518110611e8b57611e8a614258565b5b60200260200101516040018181525050828181518110611eae57611ead614258565b5b6020026020010151600001519150611f2682848381518110611ed357611ed2614258565b5b602002602001015160200151858481518110611ef257611ef1614258565b5b602002602001015160400151600760009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16613152565b8080611f3190614446565b915050611de8565b5060005b8989905081101561206157368a8a83818110611f5c57611f5b614258565b5b9050602002810190611f6e91906145b3565b9050600760009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16633af2c012826000016020810190611fc19190613df8565b8360200135848060400190611fd691906145db565b868060600190611fe6919061463e565b88608001358e8a60a001358b60c001356040518b63ffffffff1660e01b815260040161201b9a99989796959493929190614bbd565b600060405180830381600087803b15801561203557600080fd5b505af1158015612049573d6000803e3d6000fd5b5050505050808061205990614446565b915050611f3d565b5060005b8251811080156120935750600083828151811061208557612084614258565b5b602002602001015160200151115b156122a3578281815181106120ab576120aa614258565b5b602002602001015160000151915060008273ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b81526004016120f49190613a61565b60206040518083038186803b15801561210c57600080fd5b505afa158015612120573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121449190614c62565b9050600081111561217c5761217a33828573ffffffffffffffffffffffffffffffffffffffff166133e29092919063ffffffff16565b505b6000600360169054906101000a900461ffff1661ffff16600360149054906101000a900461ffff1661ffff16838786815181106121bc576121bb614258565b5b6020026020010151602001516121d291906143a6565b6121dc9190614864565b6121e691906148ed565b905061221e818685815181106121ff576121fe614258565b5b6020026020010151600001518c8c8c906122199190614e80565b612a3e565b809b50819250505061224f85848151811061223c5761223b614258565b5b6020026020010151600001518883613516565b61228e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161228590614ee1565b60405180910390fd5b5050808061229b90614446565b915050612065565b5086925050509695505050505050565b600080600090506000805b8989905081101561237b578989828181106122dc576122db614258565b5b90506020028101906122ee91906145b3565b608001358a8a8381811061230557612304614258565b5b905060200281019061231791906145b3565b602001356123259190614350565b836123309190614350565b925089898281811061234557612344614258565b5b905060200281019061235791906145b3565b60200135826123669190614350565b9150808061237390614446565b9150506122be565b50600360169054906101000a900461ffff1661ffff16600360149054906101000a900461ffff1661ffff16826123b19190614864565b6123bb91906148ed565b9050612430818a8a60008181106123d5576123d4614258565b5b90506020028101906123e791906145b3565b80604001906123f691906145db565b600081811061240857612407614258565b5b905060200201602081019061241d9190613df8565b878a8a9061242b9190614e80565b612a3e565b809650819250505060008989600081811061244e5761244d614258565b5b905060200281019061246091906145b3565b806040019061246f91906145db565b600081811061248157612480614258565b5b90506020020160208101906124969190613df8565b90506124c6818484600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16613152565b61252c8a8a60008181106124dd576124dc614258565b5b90506020028101906124ef91906145b3565b80604001906124fe91906145db565b60008181106125105761250f614258565b5b90506020020160208101906125259190613df8565b8684613516565b61256b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161256290614ee1565b60405180910390fd5b60005b8a8a90508110156126ac57368b8b8381811061258d5761258c614258565b5b905060200281019061259f91906145b3565b9050600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663c219a14d8280604001906125ef91906145db565b600081811061260157612600614258565b5b90506020020160208101906126169190613df8565b8360000160208101906126299190613df8565b846020013585806060019061263e919061463e565b87608001358e6040518863ffffffff1660e01b81526004016126669796959493929190614f01565b600060405180830381600087803b15801561268057600080fd5b505af1158015612694573d6000803e3d6000fd5b505050505080806126a490614446565b91505061256e565b508593505050509695505050505050565b6000806126ca8888612d60565b905060005b8151811080156126fd575060008282815181106126ef576126ee614258565b5b602002602001015160200151115b156128ed57600360169054906101000a900461ffff1661ffff16600360149054906101000a900461ffff1661ffff1683838151811061273f5761273e614258565b5b6020026020010151604001516127559190614864565b61275f91906148ed565b82828151811061277257612771614258565b5b60200260200101516040018181525050600082828151811061279757612796614258565b5b602002602001015160000151905061280f818484815181106127bc576127bb614258565b5b6020026020010151602001518585815181106127db576127da614258565b5b602002602001015160400151600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16613152565b600083838151811061282457612823614258565b5b60200260200101516040015190506128688185858151811061284957612848614258565b5b602002602001015160000151898c8c906128639190614e80565b612a3e565b809850819250505061289984848151811061288657612885614258565b5b6020026020010151600001518783613516565b6128d8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016128cf90614ee1565b60405180910390fd5b505080806128e590614446565b9150506126cf565b5060005b88889050811015612a2f57368989838181106129105761290f614258565b5b905060200281019061292291906145b3565b9050600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663c219a14d82806040019061297291906145db565b600081811061298457612983614258565b5b90506020020160208101906129999190613df8565b8360000160208101906129ac9190613df8565b84602001358580606001906129c1919061463e565b87608001358c6040518863ffffffff1660e01b81526004016129e99796959493929190614f01565b600060405180830381600087803b158015612a0357600080fd5b505af1158015612a17573d6000803e3d6000fd5b50505050508080612a2790614446565b9150506128f1565b50839150509695505050505050565b60008060008351148015612a715750600360189054906101000a900467ffffffffffffffff1667ffffffffffffffff1684105b15612a8157858491509150612d57565b600360189054906101000a900467ffffffffffffffff1667ffffffffffffffff16841015612d4b5760005b8351811015612d45578573ffffffffffffffffffffffffffffffffffffffff16848281518110612adf57612ade614258565b5b6020026020010151600081518110612afa57612af9614258565b5b602002602001015173ffffffffffffffffffffffffffffffffffffffff16148015612bcd5750600460019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16848281518110612b6c57612b6b614258565b5b60200260200101516001868481518110612b8957612b88614258565b5b602002602001015151612b9c91906143a6565b81518110612bad57612bac614258565b5b602002602001015173ffffffffffffffffffffffffffffffffffffffff16145b15612d32576000600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663fbd4122a89878581518110612c2657612c25614258565b5b60200260200101516040518363ffffffff1660e01b8152600401612c4b929190614ff1565b604080518083038186803b158015612c6257600080fd5b505afa158015612c76573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c9a9190615021565b5090506000819050600360189054906101000a900467ffffffffffffffff1667ffffffffffffffff168188612ccf9190614350565b1115612d1d5786600360189054906101000a900467ffffffffffffffff1667ffffffffffffffff16612d0191906143a6565b905081818a612d109190614864565b612d1a91906148ed565b98505b8087612d299190614350565b96505050612d45565b8080612d3d90614446565b915050612aac565b50612d50565b600095505b8584915091505b94509492505050565b60608282905067ffffffffffffffff811115612d7f57612d7e614c8f565b5b604051908082528060200260200182016040528015612db857816020015b612da5613880565b815260200190600190039081612d9d5790505b50905060005b8383905081101561314b5760005b848490508110156131375736858584818110612deb57612dea614258565b5b9050602002810190612dfd91906145b3565b9050808060400190612e0f91906145db565b6001838060400190612e2191906145db565b9050612e2d91906143a6565b818110612e3d57612e3c614258565b5b9050602002016020810190612e529190613df8565b73ffffffffffffffffffffffffffffffffffffffff16848381518110612e7b57612e7a614258565b5b60200260200101516000015173ffffffffffffffffffffffffffffffffffffffff161415612f7d576001818060400190612eb591906145db565b90501115612ef9578060a00135848381518110612ed557612ed4614258565b5b6020026020010151602001818151612eed9190614350565b91508181525050612f77565b80608001358160200135612f0d9190614350565b848381518110612f2057612f1f614258565b5b6020026020010151602001818151612f389190614350565b915081815250508060200135848381518110612f5757612f56614258565b5b6020026020010151604001818151612f6f9190614350565b915081815250505b50613137565b6000848381518110612f9257612f91614258565b5b602002602001015160200151148015612fcd575060008160a001351180612fcc5750600081608001358260200135612fca9190614350565b115b5b1561312357808060400190612fe291906145db565b6001838060400190612ff491906145db565b905061300091906143a6565b8181106130105761300f614258565b5b90506020020160208101906130259190613df8565b84838151811061303857613037614258565b5b60200260200101516000019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050600181806040019061308891906145db565b905011156130bd578060a001358483815181106130a8576130a7614258565b5b6020026020010151602001818152505061311d565b806080013581602001356130d19190614350565b8483815181106130e4576130e3614258565b5b60200260200101516020018181525050806020013584838151811061310c5761310b614258565b5b602002602001015160400181815250505b50613137565b50808061312f90614446565b915050612dcc565b50808061314390614446565b915050612dbe565b5092915050565b828473ffffffffffffffffffffffffffffffffffffffff1663dd62ed3e33306040518363ffffffff1660e01b815260040161318e929190615061565b60206040518083038186803b1580156131a657600080fd5b505afa1580156131ba573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131de9190614c62565b101561321f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401613216906150fc565b60405180910390fd5b818361322b9190614350565b8473ffffffffffffffffffffffffffffffffffffffff166370a08231336040518263ffffffff1660e01b81526004016132649190613a61565b60206040518083038186803b15801561327c57600080fd5b505afa158015613290573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132b49190614c62565b10156132f5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016132ec90615168565b60405180910390fd5b613300843085613516565b61333f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401613336906151d4565b60405180910390fd5b828473ffffffffffffffffffffffffffffffffffffffff1663dd62ed3e30846040518363ffffffff1660e01b815260040161337b929190615061565b60206040518083038186803b15801561339357600080fd5b505afa1580156133a7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133cb9190614c62565b10156133dc576133db8482613695565b5b50505050565b60008060008573ffffffffffffffffffffffffffffffffffffffff1685856040516024016134119291906151f4565b6040516020818303038152906040527fa9059cbb000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505060405161349b919061528c565b6000604051808303816000865af19150503d80600081146134d8576040519150601f19603f3d011682016040523d82523d6000602084013e6134dd565b606091505b509150915081801561350b575060008151148061350a57508080602001905181019061350991906152b8565b5b5b925050509392505050565b6000833b61352357600080fd5b60008473ffffffffffffffffffffffffffffffffffffffff16338585604051602401613551939291906152e5565b6040516020818303038152906040527f23b872dd000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040516135db919061528c565b6000604051808303816000865af19150503d8060008114613618576040519150601f19603f3d011682016040523d82523d6000602084013e61361d565b606091505b505090503d60008114613637576020811461364057600080fd5b6001925061364c565b60206000803e60005192505b508061368d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161368490615368565b60405180910390fd5b509392505050565b600082905060007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90506136ea83828473ffffffffffffffffffffffffffffffffffffffff166136f19092919063ffffffff16565b5050505050565b60008060008573ffffffffffffffffffffffffffffffffffffffff1685856040516024016137209291906151f4565b6040516020818303038152906040527f095ea7b3000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040516137aa919061528c565b6000604051808303816000865af19150503d80600081146137e7576040519150601f19603f3d011682016040523d82523d6000602084013e6137ec565b606091505b509150915081801561381a575060008151148061381957508080602001905181019061381891906152b8565b5b5b925050509392505050565b82805482825590600052602060002090810192821561386f579160200282015b8281111561386e5782518290600261385e9291906138b7565b5091602001919060010190613845565b5b50905061387c9190613941565b5090565b6040518060600160405280600073ffffffffffffffffffffffffffffffffffffffff16815260200160008152602001600081525090565b828054828255906000526020600020908101928215613930579160200282015b8281111561392f5782518260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550916020019190600101906138d7565b5b50905061393d9190613965565b5090565b5b8082111561396157600081816139589190613982565b50600101613942565b5090565b5b8082111561397e576000816000905550600101613966565b5090565b50805460008255906000526020600020908101906139a09190613965565b50565b600082825260208201905092915050565b7f4e6f6e2d70617961626c65000000000000000000000000000000000000000000600082015250565b60006139ea600b836139a3565b91506139f5826139b4565b602082019050919050565b60006020820190508181036000830152613a19816139dd565b9050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000613a4b82613a20565b9050919050565b613a5b81613a40565b82525050565b6000602082019050613a766000830184613a52565b92915050565b6000819050919050565b6000613aa1613a9c613a9784613a20565b613a7c565b613a20565b9050919050565b6000613ab382613a86565b9050919050565b6000613ac582613aa8565b9050919050565b613ad581613aba565b82525050565b6000602082019050613af06000830184613acc565b92915050565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b600080fd5b60008083601f840112613b2f57613b2e613b0a565b5b8235905067ffffffffffffffff811115613b4c57613b4b613b0f565b5b602083019150836020820283011115613b6857613b67613b14565b5b9250929050565b60008115159050919050565b613b8481613b6f565b8114613b8f57600080fd5b50565b600081359050613ba181613b7b565b92915050565b6000613bb282613a20565b9050919050565b613bc281613ba7565b8114613bcd57600080fd5b50565b600081359050613bdf81613bb9565b92915050565b60008060008060608587031215613bff57613bfe613b00565b5b600085013567ffffffffffffffff811115613c1d57613c1c613b05565b5b613c2987828801613b19565b94509450506020613c3c87828801613b92565b9250506040613c4d87828801613bd0565b91505092959194509250565b6000819050919050565b613c6c81613c59565b82525050565b6000602082019050613c876000830184613c63565b92915050565b600067ffffffffffffffff82169050919050565b613caa81613c8d565b8114613cb557600080fd5b50565b600081359050613cc781613ca1565b92915050565b600060208284031215613ce357613ce2613b00565b5b6000613cf184828501613cb8565b91505092915050565b6000613d0582613aa8565b9050919050565b613d1581613cfa565b82525050565b6000602082019050613d306000830184613d0c565b92915050565b613d3f81613c8d565b82525050565b6000602082019050613d5a6000830184613d36565b92915050565b613d6981613c59565b8114613d7457600080fd5b50565b600081359050613d8681613d60565b92915050565b60008060408385031215613da357613da2613b00565b5b6000613db185828601613d77565b9250506020613dc285828601613d77565b9150509250929050565b613dd581613a40565b8114613de057600080fd5b50565b600081359050613df281613dcc565b92915050565b600060208284031215613e0e57613e0d613b00565b5b6000613e1c84828501613de3565b91505092915050565b6000613e3082613aa8565b9050919050565b613e4081613e25565b82525050565b6000602082019050613e5b6000830184613e37565b92915050565b60008083601f840112613e7757613e76613b0a565b5b8235905067ffffffffffffffff811115613e9457613e93613b0f565b5b602083019150836020820283011115613eb057613eaf613b14565b5b9250929050565b60008083601f840112613ecd57613ecc613b0a565b5b8235905067ffffffffffffffff811115613eea57613ee9613b0f565b5b602083019150836020820283011115613f0657613f05613b14565b5b9250929050565b600080600080600060608688031215613f2957613f28613b00565b5b600086013567ffffffffffffffff811115613f4757613f46613b05565b5b613f5388828901613e61565b9550955050602086013567ffffffffffffffff811115613f7657613f75613b05565b5b613f8288828901613eb7565b93509350506040613f9588828901613de3565b9150509295509295909350565b6000613fad82613aa8565b9050919050565b613fbd81613fa2565b82525050565b6000602082019050613fd86000830184613fb4565b92915050565b600080600080600060608688031215613ffa57613ff9613b00565b5b600086013567ffffffffffffffff81111561401857614017613b05565b5b61402488828901613b19565b9550955050602086013567ffffffffffffffff81111561404757614046613b05565b5b61405388828901613eb7565b9350935050604061406688828901613de3565b9150509295509295909350565b600061407e82613aa8565b9050919050565b61408e81614073565b82525050565b60006020820190506140a96000830184614085565b92915050565b600080604083850312156140c6576140c5613b00565b5b60006140d485828601613de3565b92505060206140e585828601613de3565b9150509250929050565b600061ffff82169050919050565b614106816140ef565b811461411157600080fd5b50565b600081359050614123816140fd565b92915050565b60006020828403121561413f5761413e613b00565b5b600061414d84828501614114565b91505092915050565b61415f816140ef565b82525050565b600060208201905061417a6000830184614156565b92915050565b7f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572600082015250565b60006141b66020836139a3565b91506141c182614180565b602082019050919050565b600060208201905081810360008301526141e5816141a9565b9050919050565b7f6d6f7265207468616e2035206d65746144657461696c73000000000000000000600082015250565b60006142226017836139a3565b915061422d826141ec565b602082019050919050565b6000602082019050818103600083015261425181614215565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600080fd5b600080fd5b600080fd5b6000823560016040038336030381126142b2576142b1614287565b5b80830191505092915050565b600080833560016020038436030381126142db576142da614287565b5b80840192508235915067ffffffffffffffff8211156142fd576142fc61428c565b5b60208301925060208202360383131561431957614318614291565b5b509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600061435b82613c59565b915061436683613c59565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0382111561439b5761439a614321565b5b828201905092915050565b60006143b182613c59565b91506143bc83613c59565b9250828210156143cf576143ce614321565b5b828203905092915050565b7f57726f6e67207061796d656e744e6574776f726b496400000000000000000000600082015250565b60006144106016836139a3565b915061441b826143da565b602082019050919050565b6000602082019050818103600083015261443f81614403565b9050919050565b600061445182613c59565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82141561448457614483614321565b5b600182019050919050565b7f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160008201527f6464726573730000000000000000000000000000000000000000000000000000602082015250565b60006144eb6026836139a3565b91506144f68261448f565b604082019050919050565b6000602082019050818103600083015261451a816144de565b9050919050565b7f546865206261746368206665652076616c756520697320746f6f20686967683a60008201527f203e203225000000000000000000000000000000000000000000000000000000602082015250565b600061457d6025836139a3565b915061458882614521565b604082019050919050565b600060208201905081810360008301526145ac81614570565b9050919050565b60008235600160e0038336030381126145cf576145ce614287565b5b80830191505092915050565b600080833560016020038436030381126145f8576145f7614287565b5b80840192508235915067ffffffffffffffff82111561461a5761461961428c565b5b60208301925060208202360383131561463657614635614291565b5b509250929050565b6000808335600160200384360303811261465b5761465a614287565b5b80840192508235915067ffffffffffffffff82111561467d5761467c61428c565b5b60208301925060018202360383131561469957614698614291565b5b509250929050565b60006146ac82613aa8565b9050919050565b6146bc816146a1565b82525050565b600082825260208201905092915050565b6000819050919050565b6146e681613a40565b82525050565b60006146f883836146dd565b60208301905092915050565b60006147136020840184613de3565b905092915050565b6000602082019050919050565b600061473483856146c2565b935061473f826146d3565b8060005b85811015614778576147558284614704565b61475f88826146ec565b975061476a8361471b565b925050600181019050614743565b5085925050509392505050565b600082825260208201905092915050565b82818337600083830152505050565b6000601f19601f8301169050919050565b60006147c28385614785565b93506147cf838584614796565b6147d8836147a5565b840190509392505050565b600060e0820190506147f8600083018c6146b3565b614805602083018b613c63565b818103604083015261481881898b614728565b9050818103606083015261482d8187896147b6565b905061483c6080830186613c63565b61484960a08301856146b3565b61485660c0830184613c63565b9a9950505050505050505050565b600061486f82613c59565b915061487a83613c59565b9250817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04831182151516156148b3576148b2614321565b5b828202905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b60006148f882613c59565b915061490383613c59565b925082614913576149126148be565b5b828204905092915050565b7f4e6f7420656e6f7567682066756e647320666f7220626174636820636f6e766560008201527f7273696f6e206665657300000000000000000000000000000000000000000000602082015250565b600061497a602a836139a3565b91506149858261491e565b604082019050919050565b600060208201905081810360008301526149a98161496d565b9050919050565b600081905092915050565b50565b60006149cb6000836149b0565b91506149d6826149bb565b600082019050919050565b60006149ec826149be565b9150819050919050565b7f436f756c64206e6f742073656e642072656d61696e696e672066756e6473207460008201527f6f20746865207061796572000000000000000000000000000000000000000000602082015250565b6000614a52602b836139a3565b9150614a5d826149f6565b604082019050919050565b60006020820190508181036000830152614a8181614a45565b9050919050565b7f4e6f7420656e6f7567682066756e647300000000000000000000000000000000600082015250565b6000614abe6010836139a3565b9150614ac982614a88565b602082019050919050565b60006020820190508181036000830152614aed81614ab1565b9050919050565b614afd81613ba7565b82525050565b6000608082019050614b186000830188614af4565b8181036020830152614b2b8186886147b6565b9050614b3a6040830185613c63565b614b476060830184614af4565b9695505050505050565b7f4e6f7420656e6f7567682066756e647320666f72206261746368206665650000600082015250565b6000614b87601e836139a3565b9150614b9282614b51565b602082019050919050565b60006020820190508181036000830152614bb681614b7a565b9050919050565b600061010082019050614bd3600083018d613a52565b614be0602083018c613c63565b8181036040830152614bf3818a8c614728565b90508181036060830152614c0881888a6147b6565b9050614c176080830187613c63565b614c2460a0830186613a52565b614c3160c0830185613c63565b614c3e60e0830184613c63565b9b9a5050505050505050505050565b600081519050614c5c81613d60565b92915050565b600060208284031215614c7857614c77613b00565b5b6000614c8684828501614c4d565b91505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b614cc7826147a5565b810181811067ffffffffffffffff82111715614ce657614ce5614c8f565b5b80604052505050565b6000614cf9613af6565b9050614d058282614cbe565b919050565b600067ffffffffffffffff821115614d2557614d24614c8f565b5b602082029050602081019050919050565b600067ffffffffffffffff821115614d5157614d50614c8f565b5b602082029050602081019050919050565b6000614d75614d7084614d36565b614cef565b90508083825260208201905060208402830185811115614d9857614d97613b14565b5b835b81811015614dc15780614dad8882613de3565b845260208401935050602081019050614d9a565b5050509392505050565b600082601f830112614de057614ddf613b0a565b5b8135614df0848260208601614d62565b91505092915050565b6000614e0c614e0784614d0a565b614cef565b90508083825260208201905060208402830185811115614e2f57614e2e613b14565b5b835b81811015614e7657803567ffffffffffffffff811115614e5457614e53613b0a565b5b808601614e618982614dcb565b85526020850194505050602081019050614e31565b5050509392505050565b6000614e8d368484614df9565b905092915050565b7f426174636820666565207472616e7366657246726f6d2829206661696c656400600082015250565b6000614ecb601f836139a3565b9150614ed682614e95565b602082019050919050565b60006020820190508181036000830152614efa81614ebe565b9050919050565b600060c082019050614f16600083018a613a52565b614f236020830189613a52565b614f306040830188613c63565b8181036060830152614f438186886147b6565b9050614f526080830185613c63565b614f5f60a0830184613a52565b98975050505050505050565b600081519050919050565b6000819050602082019050919050565b6000602082019050919050565b6000614f9e82614f6b565b614fa881856146c2565b9350614fb383614f76565b8060005b83811015614fe4578151614fcb88826146ec565b9750614fd683614f86565b925050600181019050614fb7565b5085935050505092915050565b60006040820190506150066000830185613c63565b81810360208301526150188184614f93565b90509392505050565b6000806040838503121561503857615037613b00565b5b600061504685828601614c4d565b925050602061505785828601614c4d565b9150509250929050565b60006040820190506150766000830185613a52565b6150836020830184613a52565b9392505050565b7f496e73756666696369656e7420616c6c6f77616e636520666f7220626174636860008201527f20746f2070617900000000000000000000000000000000000000000000000000602082015250565b60006150e66027836139a3565b91506150f18261508a565b604082019050919050565b60006020820190508181036000830152615115816150d9565b9050919050565b7f4e6f7420656e6f7567682066756e64732c20696e636c7564696e672066656573600082015250565b60006151526020836139a3565b915061515d8261511c565b602082019050919050565b6000602082019050818103600083015261518181615145565b9050919050565b7f7061796d656e74207472616e7366657246726f6d2829206661696c6564000000600082015250565b60006151be601d836139a3565b91506151c982615188565b602082019050919050565b600060208201905081810360008301526151ed816151b1565b9050919050565b60006040820190506152096000830185613a52565b6152166020830184613c63565b9392505050565b600081519050919050565b60005b8381101561524657808201518184015260208101905061522b565b83811115615255576000848401525b50505050565b60006152668261521d565b61527081856149b0565b9350615280818560208601615228565b80840191505092915050565b6000615298828461525b565b915081905092915050565b6000815190506152b281613b7b565b92915050565b6000602082840312156152ce576152cd613b00565b5b60006152dc848285016152a3565b91505092915050565b60006060820190506152fa6000830186613a52565b6153076020830185613a52565b6153146040830184613c63565b949350505050565b7f7472616e7366657246726f6d282920686173206265656e207265766572746564600082015250565b60006153526020836139a3565b915061535d8261531c565b602082019050919050565b6000602082019050818103600083015261538181615345565b905091905056fea26469706673582212207a94adaf919c0e13cce412e4c26773542b3f55f9201a267be3de328ccc8ec67e64736f6c63430008090033000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004e64c2d06d19d13061e62e291b2c4e9fe5679b93

Deployed Bytecode

0x6080604052600436106101a05760003560e01c80638da2f6d8116100ec578063b677bf0e1161008a578063e695f75c11610064578063e695f75c14610606578063f2fde38b1461062f578063f4d0841214610658578063f8b823e41461068157610201565b8063b677bf0e14610575578063dae3d6bb146105b2578063df6557ec146105dd57610201565b8063946647f1116100c6578063946647f1146104a557806397aa096e146104d05780639af8a581146104fb578063a4b519ff1461053857610201565b80638da2f6d8146104355780638da5cb5b1461045e57806392cddb911461048957610201565b80633085df631161015957806360be74e61161013357806360be74e6146103a1578063715018a6146103cc5780637abff543146103e357806381fe66ce1461040c57610201565b80633085df631461030b578063333ec062146103485780634465549a1461037857610201565b80630917377f146102065780630a9157c4146102315780630e48b8db1461025c57806316ac30a91461028c5780632e2f0ca0146102b55780632fae95bf146102e057610201565b3661020157600860149054906101000a900460ff16806101c05750600034145b6101ff576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016101f690613a00565b60405180910390fd5b005b600080fd5b34801561021257600080fd5b5061021b6106ac565b6040516102289190613a61565b60405180910390f35b34801561023d57600080fd5b506102466106d2565b6040516102539190613adb565b60405180910390f35b61027660048036038101906102719190613be5565b6106f8565b6040516102839190613c72565b60405180910390f35b34801561029857600080fd5b506102b360048036038101906102ae9190613ccd565b610712565b005b3480156102c157600080fd5b506102ca6107ba565b6040516102d79190613d1b565b60405180910390f35b3480156102ec57600080fd5b506102f56107e0565b6040516103029190613d45565b60405180910390f35b34801561031757600080fd5b50610332600480360381019061032d9190613d8c565b6107fa565b60405161033f9190613a61565b60405180910390f35b610362600480360381019061035d9190613be5565b610854565b60405161036f9190613c72565b60405180910390f35b34801561038457600080fd5b5061039f600480360381019061039a9190613df8565b61086e565b005b3480156103ad57600080fd5b506103b661092e565b6040516103c39190613e46565b60405180910390f35b3480156103d857600080fd5b506103e1610954565b005b3480156103ef57600080fd5b5061040a60048036038101906104059190613df8565b6109dc565b005b34801561041857600080fd5b50610433600480360381019061042e9190613df8565b610a9c565b005b34801561044157600080fd5b5061045c60048036038101906104579190613df8565b610b5c565b005b34801561046a57600080fd5b50610473610c1c565b6040516104809190613a61565b60405180910390f35b6104a3600480360381019061049e9190613f0d565b610c45565b005b3480156104b157600080fd5b506104ba610f07565b6040516104c79190613fc3565b60405180910390f35b3480156104dc57600080fd5b506104e5610f2d565b6040516104f29190613a61565b60405180910390f35b34801561050757600080fd5b50610522600480360381019061051d9190613fde565b610f53565b60405161052f9190613c72565b60405180910390f35b34801561054457600080fd5b5061055f600480360381019061055a9190613fde565b610f6f565b60405161056c9190613c72565b60405180910390f35b34801561058157600080fd5b5061059c60048036038101906105979190613fde565b610f8b565b6040516105a99190613c72565b60405180910390f35b3480156105be57600080fd5b506105c7610fa7565b6040516105d49190614094565b60405180910390f35b3480156105e957600080fd5b5061060460048036038101906105ff91906140af565b610fcd565b005b34801561061257600080fd5b5061062d60048036038101906106289190613df8565b61119f565b005b34801561063b57600080fd5b5061065660048036038101906106519190613df8565b61125f565b005b34801561066457600080fd5b5061067f600480360381019061067a9190614129565b611357565b005b34801561068d57600080fd5b5061069661143b565b6040516106a39190614165565b60405180910390f35b600460019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600061070885858560008661144f565b9050949350505050565b61071a61188c565b73ffffffffffffffffffffffffffffffffffffffff16610738610c1c565b73ffffffffffffffffffffffffffffffffffffffff161461078e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610785906141cc565b60405180910390fd5b80600360186101000a81548167ffffffffffffffff021916908367ffffffffffffffff16021790555050565b600760009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600360189054906101000a900467ffffffffffffffff1681565b6006828154811061080a57600080fd5b90600052602060002001818154811061082257600080fd5b906000526020600020016000915091509054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000610864858585600086611894565b9050949350505050565b61087661188c565b73ffffffffffffffffffffffffffffffffffffffff16610894610c1c565b73ffffffffffffffffffffffffffffffffffffffff16146108ea576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108e1906141cc565b60405180910390fd5b80600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b600860009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b61095c61188c565b73ffffffffffffffffffffffffffffffffffffffff1661097a610c1c565b73ffffffffffffffffffffffffffffffffffffffff16146109d0576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016109c7906141cc565b60405180910390fd5b6109da6000611d0d565b565b6109e461188c565b73ffffffffffffffffffffffffffffffffffffffff16610a02610c1c565b73ffffffffffffffffffffffffffffffffffffffff1614610a58576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610a4f906141cc565b60405180910390fd5b80600860006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b610aa461188c565b73ffffffffffffffffffffffffffffffffffffffff16610ac2610c1c565b73ffffffffffffffffffffffffffffffffffffffff1614610b18576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b0f906141cc565b60405180910390fd5b80600760006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b610b6461188c565b73ffffffffffffffffffffffffffffffffffffffff16610b82610c1c565b73ffffffffffffffffffffffffffffffffffffffff1614610bd8576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610bcf906141cc565b60405180910390fd5b80600260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60068585905010610c8b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610c8290614238565b60405180910390fd5b6000805b86869050811015610efe5736878783818110610cae57610cad614258565b5b9050602002810190610cc09190614296565b9050600081600001351415610cfd57610ceb818060200190610ce291906142be565b85898989611dd1565b83610cf69190614350565b9250610eea565b600181600001351415610d3857610d26818060200190610d1d91906142be565b888887896122b3565b83610d319190614350565b9250610ee9565b600281600001351415610d7357610d61818060200190610d5891906142be565b888887896126bd565b83610d6c9190614350565b9250610ee8565b600381600001351415610e6b576004888860018b8b9050610d9491906143a6565b818110610da457610da3614258565b5b9050602002810190610db69190614296565b600001351415610ddc576000600460006101000a81548160ff0219169083151502179055505b610dfd818060200190610def91906142be565b600089899050148688611894565b83610e089190614350565b92506004888860018b8b9050610e1e91906143a6565b818110610e2e57610e2d614258565b5b9050602002810190610e409190614296565b600001351415610e66576001600460006101000a81548160ff0219169083151502179055505b610ee7565b600481600001351415610eab57610e99818060200190610e8b91906142be565b60008989905014868861144f565b83610ea49190614350565b9250610ee6565b6040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610edd90614426565b60405180910390fd5b5b5b5b5b508080610ef690614446565b915050610c8f565b50505050505050565b600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000610f64868686866000876122b3565b905095945050505050565b6000610f80868686866000876126bd565b905095945050505050565b6000610f9c86866000878787611dd1565b905095945050505050565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b610fd561188c565b73ffffffffffffffffffffffffffffffffffffffff16610ff3610c1c565b73ffffffffffffffffffffffffffffffffffffffff1614611049576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611040906141cc565b60405180910390fd5b81600560006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555080600460016101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060405180602001604052806040518060400160405280600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001600460019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815250815250600690600161119a929190613825565b505050565b6111a761188c565b73ffffffffffffffffffffffffffffffffffffffff166111c5610c1c565b73ffffffffffffffffffffffffffffffffffffffff161461121b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611212906141cc565b60405180910390fd5b80600360006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b61126761188c565b73ffffffffffffffffffffffffffffffffffffffff16611285610c1c565b73ffffffffffffffffffffffffffffffffffffffff16146112db576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016112d2906141cc565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141561134b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161134290614501565b60405180910390fd5b61135481611d0d565b50565b61135f61188c565b73ffffffffffffffffffffffffffffffffffffffff1661137d610c1c565b73ffffffffffffffffffffffffffffffffffffffff16146113d3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016113ca906141cc565b60405180910390fd5b60c88161ffff16111561141b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161141290614593565b60405180910390fd5b80600360146101000a81548161ffff021916908361ffff16021790555050565b600360149054906101000a900461ffff1681565b6000804790506001600860146101000a81548160ff02191690831515021790555060005b87879050811015611592573688888381811061149257611491614258565b5b90506020028101906114a491906145b3565b9050600860009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663ac473c8a478360000160208101906114f89190613df8565b846020013585806040019061150d91906145db565b87806060019061151d919061463e565b89608001358e8b60c001356040518b63ffffffff1660e01b815260040161154c999897969594939291906147e3565b6000604051808303818588803b15801561156557600080fd5b505af1158015611579573d6000803e3d6000fd5b505050505050808061158a90614446565b915050611473565b506000600360169054906101000a900461ffff1661ffff16600360149054906101000a900461ffff1661ffff1647846115cb91906143a6565b6115d59190614864565b6115df91906148ed565b905060001515861515141561172c5761172381600660008154811061160757611606614258565b5b9060005260206000200160008154811061162457611623614258565b5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16876006805480602002602001604051908101604052809291908181526020016000905b8282101561171a5783829060005260206000200180548060200260200160405190810160405280929190818152602001828054801561170657602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190600101908083116116bc575b505050505081526020019060010190611672565b50505050612a3e565b80965081925050505b8047101561176f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161176690614990565b60405180910390fd5b8373ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f193505050501580156117b5573d6000803e3d6000fd5b5060003373ffffffffffffffffffffffffffffffffffffffff16476040516117dc906149e1565b60006040518083038185875af1925050503d8060008114611819576040519150601f19603f3d011682016040523d82523d6000602084013e61181e565b606091505b5050905080611862576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161185990614a68565b60405180910390fd5b6000600860146101000a81548160ff02191690831515021790555085935050505095945050505050565b600033905090565b6000806000905060005b87879050811015611a1a57368888838181106118bd576118bc614258565b5b90506020028101906118cf91906145b3565b9050806080013581602001356118e59190614350565b471015611927576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161191e90614ad4565b60405180910390fd5b8060200135836119379190614350565b9250600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663b868980b8260800135836020013561198b9190614350565b83600001602081019061199e9190613df8565b8480606001906119ae919061463e565b86608001358b6040518763ffffffff1660e01b81526004016119d4959493929190614b03565b6000604051808303818588803b1580156119ed57600080fd5b505af1158015611a01573d6000803e3d6000fd5b5050505050508080611a1290614446565b91505061189e565b50600360169054906101000a900461ffff1661ffff16600360149054906101000a900461ffff1661ffff1682611a509190614864565b611a5a91906148ed565b9050600015158515151415611ba757611b9e816006600081548110611a8257611a81614258565b5b90600052602060002001600081548110611a9f57611a9e614258565b5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16866006805480602002602001604051908101604052809291908181526020016000905b82821015611b9557838290600052602060002001805480602002602001604051908101604052809291908181526020018280548015611b8157602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019060010190808311611b37575b505050505081526020019060010190611aed565b50505050612a3e565b80955081925050505b80471015611bea576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611be190614b9d565b60405180910390fd5b8273ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f19350505050158015611c30573d6000803e3d6000fd5b50600460009054906101000a900460ff168015611c4d5750600047115b15611d005760003373ffffffffffffffffffffffffffffffffffffffff1647604051611c78906149e1565b60006040518083038185875af1925050503d8060008114611cb5576040519150601f19603f3d011682016040523d82523d6000602084013e611cba565b606091505b5050905080611cfe576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611cf590614a68565b60405180910390fd5b505b8391505095945050505050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050816000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b600080611dde8888612d60565b9050600080600090505b825181108015611e1657506000838281518110611e0857611e07614258565b5b602002602001015160200151115b15611f3957600360169054906101000a900461ffff1661ffff16600360149054906101000a900461ffff1661ffff16848381518110611e5857611e57614258565b5b602002602001015160200151611e6e9190614864565b611e7891906148ed565b838281518110611e8b57611e8a614258565b5b60200260200101516040018181525050828181518110611eae57611ead614258565b5b6020026020010151600001519150611f2682848381518110611ed357611ed2614258565b5b602002602001015160200151858481518110611ef257611ef1614258565b5b602002602001015160400151600760009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16613152565b8080611f3190614446565b915050611de8565b5060005b8989905081101561206157368a8a83818110611f5c57611f5b614258565b5b9050602002810190611f6e91906145b3565b9050600760009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16633af2c012826000016020810190611fc19190613df8565b8360200135848060400190611fd691906145db565b868060600190611fe6919061463e565b88608001358e8a60a001358b60c001356040518b63ffffffff1660e01b815260040161201b9a99989796959493929190614bbd565b600060405180830381600087803b15801561203557600080fd5b505af1158015612049573d6000803e3d6000fd5b5050505050808061205990614446565b915050611f3d565b5060005b8251811080156120935750600083828151811061208557612084614258565b5b602002602001015160200151115b156122a3578281815181106120ab576120aa614258565b5b602002602001015160000151915060008273ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b81526004016120f49190613a61565b60206040518083038186803b15801561210c57600080fd5b505afa158015612120573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121449190614c62565b9050600081111561217c5761217a33828573ffffffffffffffffffffffffffffffffffffffff166133e29092919063ffffffff16565b505b6000600360169054906101000a900461ffff1661ffff16600360149054906101000a900461ffff1661ffff16838786815181106121bc576121bb614258565b5b6020026020010151602001516121d291906143a6565b6121dc9190614864565b6121e691906148ed565b905061221e818685815181106121ff576121fe614258565b5b6020026020010151600001518c8c8c906122199190614e80565b612a3e565b809b50819250505061224f85848151811061223c5761223b614258565b5b6020026020010151600001518883613516565b61228e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161228590614ee1565b60405180910390fd5b5050808061229b90614446565b915050612065565b5086925050509695505050505050565b600080600090506000805b8989905081101561237b578989828181106122dc576122db614258565b5b90506020028101906122ee91906145b3565b608001358a8a8381811061230557612304614258565b5b905060200281019061231791906145b3565b602001356123259190614350565b836123309190614350565b925089898281811061234557612344614258565b5b905060200281019061235791906145b3565b60200135826123669190614350565b9150808061237390614446565b9150506122be565b50600360169054906101000a900461ffff1661ffff16600360149054906101000a900461ffff1661ffff16826123b19190614864565b6123bb91906148ed565b9050612430818a8a60008181106123d5576123d4614258565b5b90506020028101906123e791906145b3565b80604001906123f691906145db565b600081811061240857612407614258565b5b905060200201602081019061241d9190613df8565b878a8a9061242b9190614e80565b612a3e565b809650819250505060008989600081811061244e5761244d614258565b5b905060200281019061246091906145b3565b806040019061246f91906145db565b600081811061248157612480614258565b5b90506020020160208101906124969190613df8565b90506124c6818484600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16613152565b61252c8a8a60008181106124dd576124dc614258565b5b90506020028101906124ef91906145b3565b80604001906124fe91906145db565b60008181106125105761250f614258565b5b90506020020160208101906125259190613df8565b8684613516565b61256b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161256290614ee1565b60405180910390fd5b60005b8a8a90508110156126ac57368b8b8381811061258d5761258c614258565b5b905060200281019061259f91906145b3565b9050600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663c219a14d8280604001906125ef91906145db565b600081811061260157612600614258565b5b90506020020160208101906126169190613df8565b8360000160208101906126299190613df8565b846020013585806060019061263e919061463e565b87608001358e6040518863ffffffff1660e01b81526004016126669796959493929190614f01565b600060405180830381600087803b15801561268057600080fd5b505af1158015612694573d6000803e3d6000fd5b505050505080806126a490614446565b91505061256e565b508593505050509695505050505050565b6000806126ca8888612d60565b905060005b8151811080156126fd575060008282815181106126ef576126ee614258565b5b602002602001015160200151115b156128ed57600360169054906101000a900461ffff1661ffff16600360149054906101000a900461ffff1661ffff1683838151811061273f5761273e614258565b5b6020026020010151604001516127559190614864565b61275f91906148ed565b82828151811061277257612771614258565b5b60200260200101516040018181525050600082828151811061279757612796614258565b5b602002602001015160000151905061280f818484815181106127bc576127bb614258565b5b6020026020010151602001518585815181106127db576127da614258565b5b602002602001015160400151600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16613152565b600083838151811061282457612823614258565b5b60200260200101516040015190506128688185858151811061284957612848614258565b5b602002602001015160000151898c8c906128639190614e80565b612a3e565b809850819250505061289984848151811061288657612885614258565b5b6020026020010151600001518783613516565b6128d8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016128cf90614ee1565b60405180910390fd5b505080806128e590614446565b9150506126cf565b5060005b88889050811015612a2f57368989838181106129105761290f614258565b5b905060200281019061292291906145b3565b9050600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663c219a14d82806040019061297291906145db565b600081811061298457612983614258565b5b90506020020160208101906129999190613df8565b8360000160208101906129ac9190613df8565b84602001358580606001906129c1919061463e565b87608001358c6040518863ffffffff1660e01b81526004016129e99796959493929190614f01565b600060405180830381600087803b158015612a0357600080fd5b505af1158015612a17573d6000803e3d6000fd5b50505050508080612a2790614446565b9150506128f1565b50839150509695505050505050565b60008060008351148015612a715750600360189054906101000a900467ffffffffffffffff1667ffffffffffffffff1684105b15612a8157858491509150612d57565b600360189054906101000a900467ffffffffffffffff1667ffffffffffffffff16841015612d4b5760005b8351811015612d45578573ffffffffffffffffffffffffffffffffffffffff16848281518110612adf57612ade614258565b5b6020026020010151600081518110612afa57612af9614258565b5b602002602001015173ffffffffffffffffffffffffffffffffffffffff16148015612bcd5750600460019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16848281518110612b6c57612b6b614258565b5b60200260200101516001868481518110612b8957612b88614258565b5b602002602001015151612b9c91906143a6565b81518110612bad57612bac614258565b5b602002602001015173ffffffffffffffffffffffffffffffffffffffff16145b15612d32576000600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663fbd4122a89878581518110612c2657612c25614258565b5b60200260200101516040518363ffffffff1660e01b8152600401612c4b929190614ff1565b604080518083038186803b158015612c6257600080fd5b505afa158015612c76573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c9a9190615021565b5090506000819050600360189054906101000a900467ffffffffffffffff1667ffffffffffffffff168188612ccf9190614350565b1115612d1d5786600360189054906101000a900467ffffffffffffffff1667ffffffffffffffff16612d0191906143a6565b905081818a612d109190614864565b612d1a91906148ed565b98505b8087612d299190614350565b96505050612d45565b8080612d3d90614446565b915050612aac565b50612d50565b600095505b8584915091505b94509492505050565b60608282905067ffffffffffffffff811115612d7f57612d7e614c8f565b5b604051908082528060200260200182016040528015612db857816020015b612da5613880565b815260200190600190039081612d9d5790505b50905060005b8383905081101561314b5760005b848490508110156131375736858584818110612deb57612dea614258565b5b9050602002810190612dfd91906145b3565b9050808060400190612e0f91906145db565b6001838060400190612e2191906145db565b9050612e2d91906143a6565b818110612e3d57612e3c614258565b5b9050602002016020810190612e529190613df8565b73ffffffffffffffffffffffffffffffffffffffff16848381518110612e7b57612e7a614258565b5b60200260200101516000015173ffffffffffffffffffffffffffffffffffffffff161415612f7d576001818060400190612eb591906145db565b90501115612ef9578060a00135848381518110612ed557612ed4614258565b5b6020026020010151602001818151612eed9190614350565b91508181525050612f77565b80608001358160200135612f0d9190614350565b848381518110612f2057612f1f614258565b5b6020026020010151602001818151612f389190614350565b915081815250508060200135848381518110612f5757612f56614258565b5b6020026020010151604001818151612f6f9190614350565b915081815250505b50613137565b6000848381518110612f9257612f91614258565b5b602002602001015160200151148015612fcd575060008160a001351180612fcc5750600081608001358260200135612fca9190614350565b115b5b1561312357808060400190612fe291906145db565b6001838060400190612ff491906145db565b905061300091906143a6565b8181106130105761300f614258565b5b90506020020160208101906130259190613df8565b84838151811061303857613037614258565b5b60200260200101516000019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050600181806040019061308891906145db565b905011156130bd578060a001358483815181106130a8576130a7614258565b5b6020026020010151602001818152505061311d565b806080013581602001356130d19190614350565b8483815181106130e4576130e3614258565b5b60200260200101516020018181525050806020013584838151811061310c5761310b614258565b5b602002602001015160400181815250505b50613137565b50808061312f90614446565b915050612dcc565b50808061314390614446565b915050612dbe565b5092915050565b828473ffffffffffffffffffffffffffffffffffffffff1663dd62ed3e33306040518363ffffffff1660e01b815260040161318e929190615061565b60206040518083038186803b1580156131a657600080fd5b505afa1580156131ba573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131de9190614c62565b101561321f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401613216906150fc565b60405180910390fd5b818361322b9190614350565b8473ffffffffffffffffffffffffffffffffffffffff166370a08231336040518263ffffffff1660e01b81526004016132649190613a61565b60206040518083038186803b15801561327c57600080fd5b505afa158015613290573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132b49190614c62565b10156132f5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016132ec90615168565b60405180910390fd5b613300843085613516565b61333f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401613336906151d4565b60405180910390fd5b828473ffffffffffffffffffffffffffffffffffffffff1663dd62ed3e30846040518363ffffffff1660e01b815260040161337b929190615061565b60206040518083038186803b15801561339357600080fd5b505afa1580156133a7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133cb9190614c62565b10156133dc576133db8482613695565b5b50505050565b60008060008573ffffffffffffffffffffffffffffffffffffffff1685856040516024016134119291906151f4565b6040516020818303038152906040527fa9059cbb000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505060405161349b919061528c565b6000604051808303816000865af19150503d80600081146134d8576040519150601f19603f3d011682016040523d82523d6000602084013e6134dd565b606091505b509150915081801561350b575060008151148061350a57508080602001905181019061350991906152b8565b5b5b925050509392505050565b6000833b61352357600080fd5b60008473ffffffffffffffffffffffffffffffffffffffff16338585604051602401613551939291906152e5565b6040516020818303038152906040527f23b872dd000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040516135db919061528c565b6000604051808303816000865af19150503d8060008114613618576040519150601f19603f3d011682016040523d82523d6000602084013e61361d565b606091505b505090503d60008114613637576020811461364057600080fd5b6001925061364c565b60206000803e60005192505b508061368d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161368490615368565b60405180910390fd5b509392505050565b600082905060007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90506136ea83828473ffffffffffffffffffffffffffffffffffffffff166136f19092919063ffffffff16565b5050505050565b60008060008573ffffffffffffffffffffffffffffffffffffffff1685856040516024016137209291906151f4565b6040516020818303038152906040527f095ea7b3000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040516137aa919061528c565b6000604051808303816000865af19150503d80600081146137e7576040519150601f19603f3d011682016040523d82523d6000602084013e6137ec565b606091505b509150915081801561381a575060008151148061381957508080602001905181019061381891906152b8565b5b5b925050509392505050565b82805482825590600052602060002090810192821561386f579160200282015b8281111561386e5782518290600261385e9291906138b7565b5091602001919060010190613845565b5b50905061387c9190613941565b5090565b6040518060600160405280600073ffffffffffffffffffffffffffffffffffffffff16815260200160008152602001600081525090565b828054828255906000526020600020908101928215613930579160200282015b8281111561392f5782518260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550916020019190600101906138d7565b5b50905061393d9190613965565b5090565b5b8082111561396157600081816139589190613982565b50600101613942565b5090565b5b8082111561397e576000816000905550600101613966565b5090565b50805460008255906000526020600020908101906139a09190613965565b50565b600082825260208201905092915050565b7f4e6f6e2d70617961626c65000000000000000000000000000000000000000000600082015250565b60006139ea600b836139a3565b91506139f5826139b4565b602082019050919050565b60006020820190508181036000830152613a19816139dd565b9050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000613a4b82613a20565b9050919050565b613a5b81613a40565b82525050565b6000602082019050613a766000830184613a52565b92915050565b6000819050919050565b6000613aa1613a9c613a9784613a20565b613a7c565b613a20565b9050919050565b6000613ab382613a86565b9050919050565b6000613ac582613aa8565b9050919050565b613ad581613aba565b82525050565b6000602082019050613af06000830184613acc565b92915050565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b600080fd5b60008083601f840112613b2f57613b2e613b0a565b5b8235905067ffffffffffffffff811115613b4c57613b4b613b0f565b5b602083019150836020820283011115613b6857613b67613b14565b5b9250929050565b60008115159050919050565b613b8481613b6f565b8114613b8f57600080fd5b50565b600081359050613ba181613b7b565b92915050565b6000613bb282613a20565b9050919050565b613bc281613ba7565b8114613bcd57600080fd5b50565b600081359050613bdf81613bb9565b92915050565b60008060008060608587031215613bff57613bfe613b00565b5b600085013567ffffffffffffffff811115613c1d57613c1c613b05565b5b613c2987828801613b19565b94509450506020613c3c87828801613b92565b9250506040613c4d87828801613bd0565b91505092959194509250565b6000819050919050565b613c6c81613c59565b82525050565b6000602082019050613c876000830184613c63565b92915050565b600067ffffffffffffffff82169050919050565b613caa81613c8d565b8114613cb557600080fd5b50565b600081359050613cc781613ca1565b92915050565b600060208284031215613ce357613ce2613b00565b5b6000613cf184828501613cb8565b91505092915050565b6000613d0582613aa8565b9050919050565b613d1581613cfa565b82525050565b6000602082019050613d306000830184613d0c565b92915050565b613d3f81613c8d565b82525050565b6000602082019050613d5a6000830184613d36565b92915050565b613d6981613c59565b8114613d7457600080fd5b50565b600081359050613d8681613d60565b92915050565b60008060408385031215613da357613da2613b00565b5b6000613db185828601613d77565b9250506020613dc285828601613d77565b9150509250929050565b613dd581613a40565b8114613de057600080fd5b50565b600081359050613df281613dcc565b92915050565b600060208284031215613e0e57613e0d613b00565b5b6000613e1c84828501613de3565b91505092915050565b6000613e3082613aa8565b9050919050565b613e4081613e25565b82525050565b6000602082019050613e5b6000830184613e37565b92915050565b60008083601f840112613e7757613e76613b0a565b5b8235905067ffffffffffffffff811115613e9457613e93613b0f565b5b602083019150836020820283011115613eb057613eaf613b14565b5b9250929050565b60008083601f840112613ecd57613ecc613b0a565b5b8235905067ffffffffffffffff811115613eea57613ee9613b0f565b5b602083019150836020820283011115613f0657613f05613b14565b5b9250929050565b600080600080600060608688031215613f2957613f28613b00565b5b600086013567ffffffffffffffff811115613f4757613f46613b05565b5b613f5388828901613e61565b9550955050602086013567ffffffffffffffff811115613f7657613f75613b05565b5b613f8288828901613eb7565b93509350506040613f9588828901613de3565b9150509295509295909350565b6000613fad82613aa8565b9050919050565b613fbd81613fa2565b82525050565b6000602082019050613fd86000830184613fb4565b92915050565b600080600080600060608688031215613ffa57613ff9613b00565b5b600086013567ffffffffffffffff81111561401857614017613b05565b5b61402488828901613b19565b9550955050602086013567ffffffffffffffff81111561404757614046613b05565b5b61405388828901613eb7565b9350935050604061406688828901613de3565b9150509295509295909350565b600061407e82613aa8565b9050919050565b61408e81614073565b82525050565b60006020820190506140a96000830184614085565b92915050565b600080604083850312156140c6576140c5613b00565b5b60006140d485828601613de3565b92505060206140e585828601613de3565b9150509250929050565b600061ffff82169050919050565b614106816140ef565b811461411157600080fd5b50565b600081359050614123816140fd565b92915050565b60006020828403121561413f5761413e613b00565b5b600061414d84828501614114565b91505092915050565b61415f816140ef565b82525050565b600060208201905061417a6000830184614156565b92915050565b7f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572600082015250565b60006141b66020836139a3565b91506141c182614180565b602082019050919050565b600060208201905081810360008301526141e5816141a9565b9050919050565b7f6d6f7265207468616e2035206d65746144657461696c73000000000000000000600082015250565b60006142226017836139a3565b915061422d826141ec565b602082019050919050565b6000602082019050818103600083015261425181614215565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600080fd5b600080fd5b600080fd5b6000823560016040038336030381126142b2576142b1614287565b5b80830191505092915050565b600080833560016020038436030381126142db576142da614287565b5b80840192508235915067ffffffffffffffff8211156142fd576142fc61428c565b5b60208301925060208202360383131561431957614318614291565b5b509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600061435b82613c59565b915061436683613c59565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0382111561439b5761439a614321565b5b828201905092915050565b60006143b182613c59565b91506143bc83613c59565b9250828210156143cf576143ce614321565b5b828203905092915050565b7f57726f6e67207061796d656e744e6574776f726b496400000000000000000000600082015250565b60006144106016836139a3565b915061441b826143da565b602082019050919050565b6000602082019050818103600083015261443f81614403565b9050919050565b600061445182613c59565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82141561448457614483614321565b5b600182019050919050565b7f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160008201527f6464726573730000000000000000000000000000000000000000000000000000602082015250565b60006144eb6026836139a3565b91506144f68261448f565b604082019050919050565b6000602082019050818103600083015261451a816144de565b9050919050565b7f546865206261746368206665652076616c756520697320746f6f20686967683a60008201527f203e203225000000000000000000000000000000000000000000000000000000602082015250565b600061457d6025836139a3565b915061458882614521565b604082019050919050565b600060208201905081810360008301526145ac81614570565b9050919050565b60008235600160e0038336030381126145cf576145ce614287565b5b80830191505092915050565b600080833560016020038436030381126145f8576145f7614287565b5b80840192508235915067ffffffffffffffff82111561461a5761461961428c565b5b60208301925060208202360383131561463657614635614291565b5b509250929050565b6000808335600160200384360303811261465b5761465a614287565b5b80840192508235915067ffffffffffffffff82111561467d5761467c61428c565b5b60208301925060018202360383131561469957614698614291565b5b509250929050565b60006146ac82613aa8565b9050919050565b6146bc816146a1565b82525050565b600082825260208201905092915050565b6000819050919050565b6146e681613a40565b82525050565b60006146f883836146dd565b60208301905092915050565b60006147136020840184613de3565b905092915050565b6000602082019050919050565b600061473483856146c2565b935061473f826146d3565b8060005b85811015614778576147558284614704565b61475f88826146ec565b975061476a8361471b565b925050600181019050614743565b5085925050509392505050565b600082825260208201905092915050565b82818337600083830152505050565b6000601f19601f8301169050919050565b60006147c28385614785565b93506147cf838584614796565b6147d8836147a5565b840190509392505050565b600060e0820190506147f8600083018c6146b3565b614805602083018b613c63565b818103604083015261481881898b614728565b9050818103606083015261482d8187896147b6565b905061483c6080830186613c63565b61484960a08301856146b3565b61485660c0830184613c63565b9a9950505050505050505050565b600061486f82613c59565b915061487a83613c59565b9250817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04831182151516156148b3576148b2614321565b5b828202905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b60006148f882613c59565b915061490383613c59565b925082614913576149126148be565b5b828204905092915050565b7f4e6f7420656e6f7567682066756e647320666f7220626174636820636f6e766560008201527f7273696f6e206665657300000000000000000000000000000000000000000000602082015250565b600061497a602a836139a3565b91506149858261491e565b604082019050919050565b600060208201905081810360008301526149a98161496d565b9050919050565b600081905092915050565b50565b60006149cb6000836149b0565b91506149d6826149bb565b600082019050919050565b60006149ec826149be565b9150819050919050565b7f436f756c64206e6f742073656e642072656d61696e696e672066756e6473207460008201527f6f20746865207061796572000000000000000000000000000000000000000000602082015250565b6000614a52602b836139a3565b9150614a5d826149f6565b604082019050919050565b60006020820190508181036000830152614a8181614a45565b9050919050565b7f4e6f7420656e6f7567682066756e647300000000000000000000000000000000600082015250565b6000614abe6010836139a3565b9150614ac982614a88565b602082019050919050565b60006020820190508181036000830152614aed81614ab1565b9050919050565b614afd81613ba7565b82525050565b6000608082019050614b186000830188614af4565b8181036020830152614b2b8186886147b6565b9050614b3a6040830185613c63565b614b476060830184614af4565b9695505050505050565b7f4e6f7420656e6f7567682066756e647320666f72206261746368206665650000600082015250565b6000614b87601e836139a3565b9150614b9282614b51565b602082019050919050565b60006020820190508181036000830152614bb681614b7a565b9050919050565b600061010082019050614bd3600083018d613a52565b614be0602083018c613c63565b8181036040830152614bf3818a8c614728565b90508181036060830152614c0881888a6147b6565b9050614c176080830187613c63565b614c2460a0830186613a52565b614c3160c0830185613c63565b614c3e60e0830184613c63565b9b9a5050505050505050505050565b600081519050614c5c81613d60565b92915050565b600060208284031215614c7857614c77613b00565b5b6000614c8684828501614c4d565b91505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b614cc7826147a5565b810181811067ffffffffffffffff82111715614ce657614ce5614c8f565b5b80604052505050565b6000614cf9613af6565b9050614d058282614cbe565b919050565b600067ffffffffffffffff821115614d2557614d24614c8f565b5b602082029050602081019050919050565b600067ffffffffffffffff821115614d5157614d50614c8f565b5b602082029050602081019050919050565b6000614d75614d7084614d36565b614cef565b90508083825260208201905060208402830185811115614d9857614d97613b14565b5b835b81811015614dc15780614dad8882613de3565b845260208401935050602081019050614d9a565b5050509392505050565b600082601f830112614de057614ddf613b0a565b5b8135614df0848260208601614d62565b91505092915050565b6000614e0c614e0784614d0a565b614cef565b90508083825260208201905060208402830185811115614e2f57614e2e613b14565b5b835b81811015614e7657803567ffffffffffffffff811115614e5457614e53613b0a565b5b808601614e618982614dcb565b85526020850194505050602081019050614e31565b5050509392505050565b6000614e8d368484614df9565b905092915050565b7f426174636820666565207472616e7366657246726f6d2829206661696c656400600082015250565b6000614ecb601f836139a3565b9150614ed682614e95565b602082019050919050565b60006020820190508181036000830152614efa81614ebe565b9050919050565b600060c082019050614f16600083018a613a52565b614f236020830189613a52565b614f306040830188613c63565b8181036060830152614f438186886147b6565b9050614f526080830185613c63565b614f5f60a0830184613a52565b98975050505050505050565b600081519050919050565b6000819050602082019050919050565b6000602082019050919050565b6000614f9e82614f6b565b614fa881856146c2565b9350614fb383614f76565b8060005b83811015614fe4578151614fcb88826146ec565b9750614fd683614f86565b925050600181019050614fb7565b5085935050505092915050565b60006040820190506150066000830185613c63565b81810360208301526150188184614f93565b90509392505050565b6000806040838503121561503857615037613b00565b5b600061504685828601614c4d565b925050602061505785828601614c4d565b9150509250929050565b60006040820190506150766000830185613a52565b6150836020830184613a52565b9392505050565b7f496e73756666696369656e7420616c6c6f77616e636520666f7220626174636860008201527f20746f2070617900000000000000000000000000000000000000000000000000602082015250565b60006150e66027836139a3565b91506150f18261508a565b604082019050919050565b60006020820190508181036000830152615115816150d9565b9050919050565b7f4e6f7420656e6f7567682066756e64732c20696e636c7564696e672066656573600082015250565b60006151526020836139a3565b915061515d8261511c565b602082019050919050565b6000602082019050818103600083015261518181615145565b9050919050565b7f7061796d656e74207472616e7366657246726f6d2829206661696c6564000000600082015250565b60006151be601d836139a3565b91506151c982615188565b602082019050919050565b600060208201905081810360008301526151ed816151b1565b9050919050565b60006040820190506152096000830185613a52565b6152166020830184613c63565b9392505050565b600081519050919050565b60005b8381101561524657808201518184015260208101905061522b565b83811115615255576000848401525b50505050565b60006152668261521d565b61527081856149b0565b9350615280818560208601615228565b80840191505092915050565b6000615298828461525b565b915081905092915050565b6000815190506152b281613b7b565b92915050565b6000602082840312156152ce576152cd613b00565b5b60006152dc848285016152a3565b91505092915050565b60006060820190506152fa6000830186613a52565b6153076020830185613a52565b6153146040830184613c63565b949350505050565b7f7472616e7366657246726f6d282920686173206265656e207265766572746564600082015250565b60006153526020836139a3565b915061535d8261531c565b602082019050919050565b6000602082019050818103600083015261538181615345565b905091905056fea26469706673582212207a94adaf919c0e13cce412e4c26773542b3f55f9201a267be3de328ccc8ec67e64736f6c63430008090033

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

000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004e64c2d06d19d13061e62e291b2c4e9fe5679b93

-----Decoded View---------------
Arg [0] : _paymentErc20Proxy (address): 0x0000000000000000000000000000000000000000
Arg [1] : _paymentNativeProxy (address): 0x0000000000000000000000000000000000000000
Arg [2] : _paymentErc20ConversionProxy (address): 0x0000000000000000000000000000000000000000
Arg [3] : _paymentNativeConversionFeeProxy (address): 0x0000000000000000000000000000000000000000
Arg [4] : _chainlinkConversionPath (address): 0x0000000000000000000000000000000000000000
Arg [5] : _owner (address): 0x4E64C2d06d19D13061e62E291b2C4e9fe5679b93

-----Encoded View---------------
6 Constructor Arguments found :
Arg [0] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [1] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [2] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [3] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [4] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [5] : 0000000000000000000000004e64c2d06d19d13061e62e291b2c4e9fe5679b93


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
[ Download: CSV Export  ]
[ Download: CSV Export  ]

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