S Price: $0.569279 (+0.77%)

Contract

0x4A1767082861c0141172dffa34D4898ea7C7C23A

Overview

S Balance

Sonic LogoSonic LogoSonic Logo0 S

S Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Pull All And Cla...59901692025-01-31 7:43:2018 hrs ago1738309400IN
0x4A176708...ea7C7C23A
0 S0.006906850
Pull All And Cla...56795042025-01-28 13:24:153 days ago1738070655IN
0x4A176708...ea7C7C23A
0 S0.006906850
Pull All And Cla...56690912025-01-28 10:37:083 days ago1738060628IN
0x4A176708...ea7C7C23A
0 S0.006906850
Pull All And Cla...56685532025-01-28 10:25:443 days ago1738059944IN
0x4A176708...ea7C7C23A
0 S0.006906850
Pull All And Cla...56685432025-01-28 10:25:313 days ago1738059931IN
0x4A176708...ea7C7C23A
0 S0.006222850
Pull All And Cla...56673812025-01-28 10:04:403 days ago1738058680IN
0x4A176708...ea7C7C23A
0 S0.007590850
Pull All And Cla...56673282025-01-28 10:03:493 days ago1738058629IN
0x4A176708...ea7C7C23A
0 S0.00463350
Pull All And Cla...56673212025-01-28 10:03:433 days ago1738058623IN
0x4A176708...ea7C7C23A
0 S0.006906850
Pull All And Cla...56673182025-01-28 10:03:373 days ago1738058617IN
0x4A176708...ea7C7C23A
0 S0.007590850
Pull All And Cla...56673152025-01-28 10:03:333 days ago1738058613IN
0x4A176708...ea7C7C23A
0 S0.007590850
Pull All And Cla...56673062025-01-28 10:03:243 days ago1738058604IN
0x4A176708...ea7C7C23A
0 S0.0082914854.61538461
Pull All And Cla...49818182025-01-22 13:01:299 days ago1737550889IN
0x4A176708...ea7C7C23A
0 S0.006906850
Pull All And Cla...48922132025-01-21 23:21:4610 days ago1737501706IN
0x4A176708...ea7C7C23A
0 S0.0069063550
Pull All And Cla...46837982025-01-20 13:23:2811 days ago1737379408IN
0x4A176708...ea7C7C23A
0 S0.0081872153.92857142
Pull All And Cla...46533152025-01-20 7:30:3611 days ago1737358236IN
0x4A176708...ea7C7C23A
0 S0.006906850
Pull All And Cla...46532932025-01-20 7:30:2111 days ago1737358221IN
0x4A176708...ea7C7C23A
0 S0.006222850
Pull All And Cla...46268912025-01-20 1:59:3712 days ago1737338377IN
0x4A176708...ea7C7C23A
0 S0.006906850
Pull All And Cla...46268612025-01-20 1:59:1212 days ago1737338352IN
0x4A176708...ea7C7C23A
0 S0.006222850
Pull All And Cla...46268122025-01-20 1:58:3912 days ago1737338319IN
0x4A176708...ea7C7C23A
0 S0.006222850
Pull All And Cla...46267592025-01-20 1:58:0412 days ago1737338284IN
0x4A176708...ea7C7C23A
0 S0.006906850
Pull All And Cla...46267222025-01-20 1:57:3912 days ago1737338259IN
0x4A176708...ea7C7C23A
0 S0.006222850
Pull All And Cla...46142722025-01-20 0:12:4112 days ago1737331961IN
0x4A176708...ea7C7C23A
0 S0.0067932254.58333333
Pull All And Cla...37118292025-01-13 8:40:3318 days ago1736757633IN
0x4A176708...ea7C7C23A
0 S0.000622285
Pull All And Cla...36702602025-01-13 0:34:3119 days ago1736728471IN
0x4A176708...ea7C7C23A
0 S0.000690685
Pull All And Cla...36702432025-01-13 0:34:2419 days ago1736728464IN
0x4A176708...ea7C7C23A
0 S0.000690685
View all transactions

Parent Transaction Hash Block From To
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
GeneralTokenDispenserV1

Compiler Version
v0.8.24+commit.e11b9ed9

Optimization Enabled:
Yes with 200 runs

Other Settings:
paris EvmVersion
File 1 of 20 : GeneralTokenDispenserV1.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;

import {Ownable2Step, Ownable} from "@openzeppelin/contracts/access/Ownable2Step.sol";

import "../../../Lynx/interfaces/ILynxVersionedContract.sol";
import {IPushableDispenserV1} from "../interfaces/IPushableDispenserV1.sol";
import {IDispenserPullSourceV1} from "../interfaces/IDispenserPullSourceV1.sol";
import {TokenHandlingContractUtils} from "../../../Utils/TokenHandlingContractUtils.sol";
import {LexPoolEvents} from "../../../Lynx/interfaces/ILexPoolV1.sol";
import {AbstractPullSourceV1} from "./AbstractPullSourceV1.sol";

/**
 * @title GeneralTokenDispenserV1
 * @notice Functions as a single point for token dispensations from many sources
 */
contract GeneralTokenDispenserV1 is
  Ownable2Step,
  AbstractPullSourceV1,
  IPushableDispenserV1,
  ILynxVersionedContract
{
  string public constant CONTRACT_NAME = "GeneralTokenDispenserV1";
  string public constant CONTRACT_VERSION = "100"; // 0.1

  // ***** Enums *****

  enum DispensationPushLevel {
    NONE,
    BASE // Can push token dispensation plan but has to provide the tokens in the same tx
  }

  // ***** Events *****

  event TokenPulledForAccountFromSource(
    address indexed token,
    address indexed account,
    address indexed source,
    uint256 amount
  );
  event TokenPushedForAccount(
    string indexed dispensationKey,
    address indexed token,
    address indexed account,
    address pusher,
    uint256 round,
    uint256 amount
  );

  event IsAllClaimingPausedFlagSet(bool indexed value);
  event IsClaimingPausedForTokenFlagSet(
    address indexed token,
    bool indexed value
  );
  event PullSourceAdded(address indexed token, address indexed pullSource);
  event PullSourceRemoved(address indexed token, address indexed pullSource);
  event PusherRegistered(
    address indexed _token,
    address indexed _pusher,
    DispensationPushLevel level
  );
  event PusherUnregistered(address indexed _token, address indexed _pusher);

  event DispensationStarted(
    string indexed dispensationKey,
    address indexed token
  );
  event DispensationEnded(
    string indexed dispensationKey,
    address indexed token
  );
  event DispensationExplained(
    string indexed dispensationKey,
    address indexed token,
    string name,
    string description
  );

  event TokenAddedToEverDispensed(address indexed token);
  event TokenAddedToCurrentlyDispensed(address indexed token);
  event TokenRemovedFromCurrentlyDispensed(address indexed token);

  // ***** Modifiers *****

  modifier onlyRegisteredPusher(address _token, address _pusher) {
    require(isRegisteredPusher(_token, _pusher), "NOT_REGISTERED_PUSHER");
    _;
  }

  modifier whenClaimNotPaused(address _token) {
    require(!isAllClaimingPaused, "ALL_CLAIMING_PAUSED");
    require(!isClaimingPausedForToken[_token], "CLAIMING_PAUSED_FOR_TOKEN");
    _;
  }

  // ***** Storage *****

  // Allows to pause all claiming
  bool public isAllClaimingPaused;
  // Allows to pause claiming for specific tokens
  // token => flag
  mapping(address => bool) public isClaimingPausedForToken;

  // token => live contracts that can generate distributions events.
  mapping(address => address[]) public pullSourcesForToken;
  // token => poolSource => is pull source
  mapping(address => mapping(address => bool)) public isPullSourceForToken;

  // token => pushers
  mapping(address => address[]) public pushersForToken;

  // token => pusher => level
  mapping(address => mapping(address => DispensationPushLevel))
    public dispensationPusherLevels;

  address[] public allTokensCurrentlyDispensed;
  address[] public allTokensEverDispensed;
  // token => flag
  mapping(address => bool) public wasTokenEverDispensed;
  // token => flag
  mapping(address => bool) public isTokenCurrentlyDispensed;

  // token => account => historic amount of tokens dispensed to account
  mapping(address => mapping(address => uint256))
    public historicDispensationsForAccount;
  // token => account => historic amount of tokens dispensed to downstream dispense
  mapping(address => mapping(address => uint256))
    public downstreamDispensationsForAccount;

  // token => ongoing dispensation keys
  mapping(address => string[]) public allOngoingDispensationsForToken;

  // token => dispensation key => flag
  mapping(address => mapping(string => bool)) public isOngoingDispensation;
  // token => dispensation key => name
  mapping(address => mapping(string => string)) public dispensationNames;
  // token => dispensation key => description
  mapping(address => mapping(string => string)) public dispensationDescriptions;

  // ***** Views *****

  /**
   * @notice Returns the name of the contract
   */
  function getContractName() external pure returns (string memory) {
    return CONTRACT_NAME;
  }

  /**
   * @notice Returns the version of the contract
   * @dev units are scaled by 1000 (1,000 = 1.00, 1,120 = 1.12)
   */
  function getContractVersion() external pure returns (string memory) {
    return CONTRACT_VERSION;
  }

  function isPushableDispenserV1() external pure returns (bool) {
    return true;
  }

  function getPullSourcesForToken(
    address _token
  ) external view returns (address[] memory) {
    return pullSourcesForToken[_token];
  }

  function getPushersForToken(
    address _token
  ) external view returns (address[] memory) {
    return pushersForToken[_token];
  }

  function getDispensationPusherLevel(
    address _token,
    address _pusher
  ) external view returns (DispensationPushLevel) {
    return dispensationPusherLevels[_token][_pusher];
  }

  function getDispensationInfo(
    string calldata dispensationKey,
    address _token
  ) external view returns (string memory name, string memory description) {
    return (
      dispensationNames[_token][dispensationKey],
      dispensationDescriptions[_token][dispensationKey]
    );
  }

  function getAllTokensEverDispensed()
    external
    view
    returns (address[] memory)
  {
    return allTokensEverDispensed;
  }

  function getAllTokensCurrentlyDispensed()
    external
    view
    returns (address[] memory)
  {
    return allTokensCurrentlyDispensed;
  }

  function getAllOngoingDispensationsForToken(
    address _token
  ) external view returns (string[] memory) {
    return allOngoingDispensationsForToken[_token];
  }

  // ***** Constructor *****

  constructor() Ownable(msg.sender) {}

  // ***** Admin Functions *****

  function setDownstreamDispenser(
    address _downstreamDispenser
  ) external onlyOwner {
    setDownstreamDispenserInternal(_downstreamDispenser);
  }

  function setIsAllClaimingPaused(bool _value) external onlyOwner {
    // Validity
    require(isAllClaimingPaused != _value, "ALREADY_SET");

    // Storage
    isAllClaimingPaused = _value;

    // Event
    emit IsAllClaimingPausedFlagSet(isAllClaimingPaused);
  }

  function setIsClaimingPausedForToken(
    address _token,
    bool _value
  ) external onlyOwner {
    // Validity
    require(isClaimingPausedForToken[_token] != _value, "ALREADY_SET");

    // Storage
    isClaimingPausedForToken[_token] = _value;

    // Event
    emit IsClaimingPausedForTokenFlagSet(
      _token,
      isClaimingPausedForToken[_token]
    );
  }

  function addPullSource(
    address _token,
    address _pullSource
  ) external onlyOwner {
    // Sanity
    require(
      IDispenserPullSourceV1(_pullSource).isDispenserMatchingSourceV1(),
      "NOT_PULL_SOURCE_V1"
    );

    // Prevent duplications
    address[] storage pullSources = pullSourcesForToken[_token];
    for (uint i = 0; i < pullSources.length; i++) {
      require(pullSources[i] != _pullSource, "PULL_SOURCE_ALREADY_ADDED");
    }

    // Storage
    pullSourcesForToken[_token].push(_pullSource);
    isPullSourceForToken[_token][_pullSource] = true;

    // Internal registry
    addTokenToListsIfNeeded(_token);

    // Event
    emit PullSourceAdded(_token, _pullSource);
  }

  function removePullSource(
    address _token,
    address _pullSource
  ) external onlyOwner {
    bool pullSourceFound = false;

    // Storage
    address[] storage pullSources = pullSourcesForToken[_token];
    for (uint i = 0; i < pullSources.length; i++) {
      if (pullSources[i] == _pullSource) {
        pullSources[i] = pullSources[pullSources.length - 1];
        pullSources.pop();
        pullSourceFound = true;
        break;
      }
    }

    // Sanity
    require(pullSourceFound, "PULL_SOURCE_NOT_FOUND");

    // Storage
    isPullSourceForToken[_token][_pullSource] = false;

    // Internal registry
    removeTokenFromListsIfNeeded(_token);

    // Event
    emit PullSourceRemoved(_token, _pullSource);
  }

  function registerPusher(
    address _token,
    address _pusher,
    DispensationPushLevel level
  ) external onlyOwner {
    // Validation
    require(!isRegisteredPusher(_token, _pusher), "PUSHER_ALREADY_REGISTERED");
    require(level != DispensationPushLevel.NONE, "CANNOT_REGISTER_NONE_LEVEL");

    // Storage
    dispensationPusherLevels[_token][_pusher] = level;

    // Internal registry
    pushersForToken[_token].push(_pusher);
    addTokenToListsIfNeeded(_token);

    // Event
    emit PusherRegistered(_token, _pusher, level);
  }

  function unregisterPusher(
    address _token,
    address _pusher
  ) external onlyOwner {
    // Validation
    require(isRegisteredPusher(_token, _pusher), "PUSHER_NOT_REGISTERED");

    // Storage
    dispensationPusherLevels[_token][_pusher] = DispensationPushLevel.NONE;

    // Remove pusher from array
    address[] storage pushers = pushersForToken[_token];
    for (uint i = 0; i < pushers.length; i++) {
      if (pushers[i] == _pusher) {
        pushers[i] = pushers[pushers.length - 1];
        pushers.pop();
        break;
      }
    }

    // Internal registry
    removeTokenFromListsIfNeeded(_token);

    // Event
    emit PusherUnregistered(_token, _pusher);
  }

  function startDispensation(
    string calldata _dispensationKey,
    address _token,
    string calldata _name,
    string calldata _description
  ) external onlyOwner {
    // Validity
    require(
      !isOngoingDispensation[_token][_dispensationKey],
      "ALREADY_ONGOING"
    );

    // Storage
    isOngoingDispensation[_token][_dispensationKey] = true;
    allOngoingDispensationsForToken[_token].push(_dispensationKey);
    explainDispensationInternal(_dispensationKey, _token, _name, _description);

    // Event
    emit DispensationStarted(_dispensationKey, _token);
  }

  function endDispensation(
    string calldata _dispensationKey,
    address _token
  ) external onlyOwner {
    // Validity
    require(
      isOngoingDispensation[_token][_dispensationKey],
      "NOT_ONGOING_DISPENSATION"
    );

    // Storage
    isOngoingDispensation[_token][_dispensationKey] = false;

    string[]
      storage ongoingDispensationsForToken = allOngoingDispensationsForToken[
        _token
      ];

    for (uint i = 0; i < ongoingDispensationsForToken.length; i++) {
      if (
        keccak256(abi.encodePacked(ongoingDispensationsForToken[i])) ==
        keccak256(abi.encodePacked(_dispensationKey))
      ) {
        ongoingDispensationsForToken[i] = ongoingDispensationsForToken[
          ongoingDispensationsForToken.length - 1
        ];
        ongoingDispensationsForToken.pop();
        break;
      }
    }

    explainDispensationInternal(_dispensationKey, _token, "", "");

    // Event
    emit DispensationEnded(_dispensationKey, _token);
  }

  function explainDispensation(
    string calldata _dispensationKey,
    address _token,
    string calldata _name,
    string calldata _description
  ) external onlyOwner {
    explainDispensationInternal(_dispensationKey, _token, _name, _description);
  }

  /**
   * @notice Sweep any tokens from the contract
   * @dev Owner can sweep any token
   * @param _token The token to sweep
   * @param _amount The amount to sweep
   */
  function sweepTokens(address _token, uint256 _amount) external onlyOwner {
    sweepTokensInternal(_token, owner(), _amount);
  }

  function discreditAccount(
    address _token,
    address _account,
    uint256 _amount,
    address _destination
  ) external onlyOwner {
    discreditTokenInternal(_token, _account, _amount, _destination);
  }

  function discreditAccounts(
    address _token,
    address[] calldata _accounts,
    uint256[] calldata _amounts,
    address _destination
  ) external onlyOwner {
    // Sanity
    require(_accounts.length == _amounts.length, "LENGTH_MISMATCH");

    for (uint256 i = 0; i < _accounts.length; i++) {
      discreditTokenInternal(_token, _accounts[i], _amounts[i], _destination);
    }
  }

  // ***** Dispensation Functions *****

  function receiveDispensationPushForAccount(
    string calldata _dispensationKey,
    uint256 _dispensationRoundNumber,
    address _token,
    address _account,
    uint256 _amount
  ) external override {
    address pusher = msg.sender;
    // Ensure sender can push dispensation with given key
    require(isRegisteredPusher(_token, pusher), "PUSHER_NOT_REGISTERED");
    // Ensure dispensation is ongoing
    require(
      isOngoingDispensation[_token][_dispensationKey],
      "NOT_ONGOING_DISPENSATION"
    );

    // Sanity
    require(_amount > 0, "AMOUNT_ZERO");

    // Take tokens(if must)
    takeTokens(_token, msg.sender, _amount);

    registerDispensationPushForAccountInternal(
      _dispensationKey,
      _dispensationRoundNumber,
      _token,
      _account,
      _amount,
      pusher
    );
  }

  function receiveDispensationPushForAccounts(
    string calldata _dispensationKey,
    uint256 _dispensationRoundNumber,
    address _token,
    address[] calldata _accounts,
    uint256[] calldata _amounts
  ) external override {
    require(_accounts.length == _amounts.length, "LENGTH_MISMATCH");

    address pusher = msg.sender;

    // Ensure sender can push dispensation with given key
    require(isRegisteredPusher(_token, pusher), "PUSHER_NOT_REGISTERED");
    // Ensure dispensation is ongoing
    require(
      isOngoingDispensation[_token][_dispensationKey],
      "NOT_ONGOING_DISPENSATION"
    );

    // Calculate total amount
    uint256 totalAmount = 0;
    for (uint i = 0; i < _accounts.length; i++) {
      totalAmount += _amounts[i];
    }

    // Sanity
    require(totalAmount > 0, "AMOUNT_ZERO");

    // Take tokens
    takeTokens(_token, pusher, totalAmount);

    // Credit accounts
    for (uint256 i = 0; i < _accounts.length; i++) {
      registerDispensationPushForAccountInternal(
        _dispensationKey,
        _dispensationRoundNumber,
        _token,
        _accounts[i],
        _amounts[i],
        pusher
      );
    }
  }

  // ***** Claiming Functions *****

  /**
   * @notice Will dispense all pending _token to _account
   */
  function claimTokenFor(
    address _token,
    address _account
  ) external whenClaimNotPaused(_token) {
    // Dispense
    dispensePendingTokenToOwnerInternal(_token, _account);
  }

  /**
   * @notice Will pull from all known sources and then dispense all pending _token to _account
   */
  function pullAllAndClaimFor(
    address _token,
    address _account
  ) external whenClaimNotPaused(_token) {
    // Pull from all sources
    _pullFromAllSourcesAndRegisterInternal(_token, _account);

    // Dispense
    dispensePendingTokenToOwnerInternal(_token, _account);
  }

  /**
   * @notice Will pull from given _pullSources and then dispense all pending _token to _account
   */
  function pullSomeAndClaimFor(
    address _token,
    address _account,
    address[] calldata pullSources
  ) external whenClaimNotPaused(_token) {
    require(pullSources.length > 0, "NO_SOURCES_PROVIDED");

    // Pull from sources
    pullFromSomeSourcesAndRegisterInternal(pullSources, _token, _account);

    // Dispense
    dispensePendingTokenToOwnerInternal(_token, _account);
  }

  // ***** Internal Dispensation Functions *****

  /**
   * @notice Will send all of _account pending _token to _account
   */
  function dispensePendingTokenToOwnerInternal(
    address _token,
    address _account
  ) internal {
    dispensePendingTokenToReceiverInternal(_token, _account, _account);
  }

  /**
   * @notice Registers the dispensation in the historic records
   */
  function onTokenDispensedToReceiverInternal(
    address _token,
    address _account,
    address _receiver,
    uint256 amountDispensed
  ) internal override {
    if (_account == _receiver) {
      // Update historic dispensed amount
      historicDispensationsForAccount[_token][_account] += amountDispensed;
    } else {
      // Update downstream dispensed amount
      downstreamDispensationsForAccount[_token][_account] += amountDispensed;
    }
  }

  // ***** Internal Source Pulling Functions *****

  /**
   * @notice calls 'pullFromSourceAndRegisterInternal' for each listed source for _token
   */
  function _pullFromAllSourcesAndRegisterInternal(
    address _token,
    address _account
  ) internal override returns (uint256 externalPulledAmount) {
    address[] memory pullSources = pullSourcesForToken[address(_token)];

    externalPulledAmount = 0;

    for (uint i = 0; i < pullSources.length; i++) {
      externalPulledAmount += pullFromSourceAndRegisterInternal(
        IDispenserPullSourceV1(pullSources[i]),
        _token,
        _account
      );
    }
  }

  /**
   * @notice calls 'pullFromSourceAndRegisterInternal' for each listed source for _token
   */
  function _getExternalPendingAmountForAccountView(
    address _token,
    address _account
  ) internal view override returns (uint256 externalPendingAmount) {
    address[] memory pullSources = pullSourcesForToken[address(_token)];

    externalPendingAmount = 0;

    for (uint i = 0; i < pullSources.length; i++) {
      externalPendingAmount += IDispenserPullSourceV1(pullSources[i])
        .getPendingAmountForAccountView(_token, _account);
    }
  }

  /**
   * @notice calls 'pullFromSourceAndRegisterInternal' for each given source
   */
  function pullFromSomeSourcesAndRegisterInternal(
    address[] calldata _sources,
    address _token,
    address _account
  ) internal {
    for (uint i = 0; i < _sources.length; i++) {
      pullFromSourceAndRegisterInternal(
        IDispenserPullSourceV1(_sources[i]),
        _token,
        _account
      );
    }
  }

  /**
   * Pulls from _source and credits gains to _account
   */
  function pullFromSourceAndRegisterInternal(
    IDispenserPullSourceV1 _source,
    address _token,
    address _account
  ) internal returns (uint256 pulledAmount) {
    pulledAmount = pullFromSourceInternal(_source, _token, _account);
    creditTokenInternal(address(_token), _account, pulledAmount);
  }

  /**
   * @notice Pulls tokens from _source for _account
   * @return pulledAmount The tokens sent to this contract for _account
   */
  function pullFromSourceInternal(
    IDispenserPullSourceV1 _source,
    address _token,
    address _account
  ) internal returns (uint256 pulledAmount) {
    // Sanity -- ensuring source is registered
    require(
      isPullSourceForToken[address(_token)][address(_source)],
      "NOT_PULL_SOURCE"
    );

    uint256 balanceBefore = getSelfBalanceInTokenInternal(_token);
    _source.pullTokenForAccount(address(_token), _account);
    uint256 balanceAfter = getSelfBalanceInTokenInternal(_token);

    pulledAmount = balanceAfter - balanceBefore;

    emit TokenPulledForAccountFromSource(
      address(_token),
      _account,
      address(_source),
      pulledAmount
    );
  }

  // ***** Internal Push Receiving Functions *****

  /**
   * @notice Registers a push dispensation
   */
  function registerDispensationPushForAccountInternal(
    string calldata dispensationKey,
    uint256 dispensationRoundNumber,
    address _token,
    address _account,
    uint256 _amount,
    address _pusher
  ) internal {
    creditTokenInternal(_token, _account, _amount);

    emit TokenPushedForAccount(
      dispensationKey,
      _token,
      _account,
      _pusher,
      dispensationRoundNumber,
      _amount
    );
  }

  // ***** Internal Access Functions *****

  function isRegisteredPusher(
    address _token,
    address _pusher
  ) internal view returns (bool) {
    DispensationPushLevel pusherLevel = dispensationPusherLevels[_token][
      _pusher
    ];

    return pusherLevel != DispensationPushLevel.NONE;
  }

  // ***** Internal Registry Functions *****

  /**
   * @notice internal hook function to be called whenever a token source/pusher is added
   * @dev Call this function after adding source/pusher.
   */
  function addTokenToListsIfNeeded(address _token) internal {
    bool isCurrentlyDispensed = isTokenCurrentlyDispensed[_token];
    // If token is not currently dispensed, there is nothing more to do
    if (isCurrentlyDispensed) {
      return;
    }

    // Add to ever dispensed list
    if (!wasTokenEverDispensed[_token]) {
      // Storage
      allTokensEverDispensed.push(_token);
      wasTokenEverDispensed[_token] = true;

      // Event
      emit TokenAddedToEverDispensed(_token);
    }

    // Add to currently dispensed list
    if (!isCurrentlyDispensed) {
      // Storage
      allTokensCurrentlyDispensed.push(_token);
      isTokenCurrentlyDispensed[_token] = true;

      // Event
      emit TokenAddedToCurrentlyDispensed(_token);
    }
  }

  /**
   * @notice internal hook function to be called whenever a token source/pusher is removed
   * @dev Call this function after removing source/pusher.
   */
  function removeTokenFromListsIfNeeded(address _token) internal {
    uint256 pullSourcesCount = pullSourcesForToken[_token].length;
    uint256 pushersCount = pushersForToken[_token].length;

    if (pullSourcesCount == 0 && pushersCount == 0) {
      // Storage

      // Remove from currently dispensed list
      for (uint i = 0; i < allTokensCurrentlyDispensed.length; i++) {
        if (allTokensCurrentlyDispensed[i] == _token) {
          allTokensCurrentlyDispensed[i] = allTokensCurrentlyDispensed[
            allTokensCurrentlyDispensed.length - 1
          ];
          allTokensCurrentlyDispensed.pop();
          break;
        }
      }

      isTokenCurrentlyDispensed[_token] = false;

      // Event
      emit TokenRemovedFromCurrentlyDispensed(_token);
    }
  }

  function explainDispensationInternal(
    string calldata dispensationKey,
    address _token,
    string memory name,
    string memory description
  ) internal {
    // Storage
    dispensationNames[_token][dispensationKey] = name;
    dispensationDescriptions[_token][dispensationKey] = description;

    // Event
    emit DispensationExplained(dispensationKey, _token, name, description);
  }
}

File 2 of 20 : Ownable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)

pragma solidity ^0.8.20;

import {Context} from "../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.
 *
 * The initial owner is set to the address provided by the deployer. 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;

    /**
     * @dev The caller account is not authorized to perform an operation.
     */
    error OwnableUnauthorizedAccount(address account);

    /**
     * @dev The owner is not a valid owner account. (eg. `address(0)`)
     */
    error OwnableInvalidOwner(address owner);

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

    /**
     * @dev Initializes the contract setting the address provided by the deployer as the initial owner.
     */
    constructor(address initialOwner) {
        if (initialOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(initialOwner);
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

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

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        if (owner() != _msgSender()) {
            revert OwnableUnauthorizedAccount(_msgSender());
        }
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby disabling 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 {
        if (newOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _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 3 of 20 : Ownable2Step.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable2Step.sol)

pragma solidity ^0.8.20;

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

/**
 * @dev Contract module which provides access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * The initial owner is specified at deployment time in the constructor for `Ownable`. This
 * can later be changed with {transferOwnership} and {acceptOwnership}.
 *
 * This module is used through inheritance. It will make available all functions
 * from parent (Ownable).
 */
abstract contract Ownable2Step is Ownable {
    address private _pendingOwner;

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

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

    /**
     * @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual override onlyOwner {
        _pendingOwner = newOwner;
        emit OwnershipTransferStarted(owner(), newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual override {
        delete _pendingOwner;
        super._transferOwnership(newOwner);
    }

    /**
     * @dev The new owner accepts the ownership transfer.
     */
    function acceptOwnership() public virtual {
        address sender = _msgSender();
        if (pendingOwner() != sender) {
            revert OwnableUnauthorizedAccount(sender);
        }
        _transferOwnership(sender);
    }
}

File 4 of 20 : IERC20Permit.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 *
 * ==== Security Considerations
 *
 * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
 * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
 * considered as an intention to spend the allowance in any specific way. The second is that because permits have
 * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
 * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
 * generally recommended is:
 *
 * ```solidity
 * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
 *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
 *     doThing(..., value);
 * }
 *
 * function doThing(..., uint256 value) public {
 *     token.safeTransferFrom(msg.sender, address(this), value);
 *     ...
 * }
 * ```
 *
 * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
 * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
 * {SafeERC20-safeTransferFrom}).
 *
 * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
 * contracts should have entry points that don't rely on permit.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     *
     * CAUTION: See Security Considerations above.
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

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

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

File 5 of 20 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

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

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

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

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

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

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

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

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

File 6 of 20 : SafeERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../utils/Address.sol";

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

    /**
     * @dev An operation with an ERC20 token failed.
     */
    error SafeERC20FailedOperation(address token);

    /**
     * @dev Indicates a failed `decreaseAllowance` request.
     */
    error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);

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

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

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

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
     * value, non-reverting calls are assumed to be successful.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
        unchecked {
            uint256 currentAllowance = token.allowance(address(this), spender);
            if (currentAllowance < requestedDecrease) {
                revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
            }
            forceApprove(token, spender, currentAllowance - requestedDecrease);
        }
    }

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

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

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

        bytes memory returndata = address(token).functionCall(data);
        if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

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

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

File 7 of 20 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)

pragma solidity ^0.8.20;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev The ETH balance of the account is not enough to perform the operation.
     */
    error AddressInsufficientBalance(address account);

    /**
     * @dev There's no code at `target` (it is not a contract).
     */
    error AddressEmptyCode(address target);

    /**
     * @dev A call to an address target failed. The target may have reverted.
     */
    error FailedInnerCall();

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

        (bool success, ) = recipient.call{value: amount}("");
        if (!success) {
            revert FailedInnerCall();
        }
    }

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        if (address(this).balance < value) {
            revert AddressInsufficientBalance(address(this));
        }
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

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

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

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
     * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
     * unsuccessful call.
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata
    ) internal view returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            // only check if target is a contract if the call was successful and the return data is empty
            // otherwise we already know that it was a contract
            if (returndata.length == 0 && target.code.length == 0) {
                revert AddressEmptyCode(target);
            }
            return returndata;
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
     * revert reason or with a default {FailedInnerCall} error.
     */
    function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            return returndata;
        }
    }

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

File 8 of 20 : Context.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)

pragma solidity ^0.8.20;

/**
 * @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;
    }

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}

File 9 of 20 : IFundingRateModel.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;

interface IFundingRateModel {
  // return value is the "funding paid by heavier side" in PRECISION per OI (heavier side) per second
  // e.g : (0.01 * PRECISION) = Paying (heavier) side (as a whole) pays 1% of funding per second for each OI unit
  function getFundingRate(
    uint256 pairId,
    uint256 openInterestLong,
    uint256 openInterestShort,
    uint256 pairMaxOpenInterest
  ) external view returns (uint256);
}

File 10 of 20 : IInterestRateModel.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;

interface IInterestRateModel {
  // Returns asset/second of interest per borrowed unit
  // e.g : (0.01 * PRECISION) = 1% of interest per second
  function getBorrowRate(uint256 utilization) external view returns (uint256);
}

File 11 of 20 : ILexPoolV1.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;

import "./LexErrors.sol";
import "./LexPoolAdminEnums.sol";
import "./IPoolAccountantV1.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

interface LexPoolStructs {
  struct PendingDeposit {
    uint256 amount;
    uint256 minAmountOut;
  }

  struct PendingRedeem {
    uint256 amount;
    uint256 minAmountOut;
    uint256 maxAmountOut;
  }
}

interface LexPoolEvents is LexPoolAdminEnums {
  event NewEpoch(
    uint256 epochId,
    int256 reportedUnrealizedPricePnL,
    uint256 exchangeRate,
    uint256 virtualUnderlyingBalance,
    uint256 totalSupply
  );

  event AddressUpdated(LexPoolAddressesEnum indexed enumCode, address a);
  event NumberUpdated(LexPoolNumbersEnum indexed enumCode, uint value);
  event DepositRequest(
    address indexed user,
    uint256 amount,
    uint256 minAmountOut,
    uint256 processingEpoch
  );
  event RedeemRequest(
    address indexed user,
    uint256 amount,
    uint256 minAmountOut,
    uint256 processingEpoch
  );
  event ProcessedDeposit(
    address indexed user,
    bool deposited,
    uint256 depositedAmount
  );
  event ProcessedRedeem(
    address indexed user,
    bool redeemed,
    uint256 withdrawnAmount // Underlying amount
  );
  event CanceledDeposit(
    address indexed user,
    uint256 epoch,
    uint256 cancelledAmount
  );
  event CanceledRedeem(
    address indexed user,
    uint256 epoch,
    uint256 cancelledAmount
  );
  event ImmediateDepositAllowedToggled(bool indexed value);
  event ImmediateDeposit(
    address indexed depositor,
    uint256 depositAmount,
    uint256 mintAmount
  );
  event ReservesWithdrawn(
    address _to,
    uint256 interestShare,
    uint256 totalFundingShare
  );
}

interface ILexPoolFunctionality is
  IERC20,
  LexPoolStructs,
  LexPoolEvents,
  LexErrors
{
  function setPoolAccountant(
    IPoolAccountantFunctionality _poolAccountant
  ) external;

  function setPnlRole(address pnl) external;

  function setMaxExtraWithdrawalAmountF(uint256 maxExtra) external;

  function setEpochsDelayDeposit(uint256 delay) external;

  function setEpochsDelayRedeem(uint256 delay) external;

  function setEpochDuration(uint256 duration) external;

  function setMinDepositAmount(uint256 amount) external;

  function toggleImmediateDepositAllowed() external;

  function reduceReserves(
    address _to
  ) external returns (uint256 interestShare, uint256 totalFundingShare);

  function requestDeposit(
    uint256 amount,
    uint256 minAmountOut,
    bytes32 domain,
    bytes32 referralCode
  ) external;

  function requestDepositViaIntent(
    address user,
    uint256 amount,
    uint256 minAmountOut,
    bytes32 domain,
    bytes32 referralCode
  ) external;

  function requestRedeem(uint256 amount, uint256 minAmountOut) external;

  function requestRedeemViaIntent(
    address user,
    uint256 amount,
    uint256 minAmountOut
  ) external;

  function processDeposit(
    address[] memory users
  )
    external
    returns (
      uint256 amountDeposited,
      uint256 amountCancelled,
      uint256 counterDeposited,
      uint256 counterCancelled
    );

  function cancelDeposits(
    address[] memory users,
    uint256[] memory epochs
  ) external;

  function processRedeems(
    address[] memory users
  )
    external
    returns (
      uint256 amountRedeemed,
      uint256 amountCancelled,
      uint256 counterDeposited,
      uint256 counterCancelled
    );

  function cancelRedeems(
    address[] memory users,
    uint256[] memory epochs
  ) external;

  function nextEpoch(
    int256 totalUnrealizedPricePnL
  ) external returns (uint256 newExchangeRate);

  function currentVirtualUtilization() external view returns (uint256);

  function currentVirtualUtilization(
    uint256 totalBorrows,
    uint256 totalReserves,
    int256 unrealizedFunding
  ) external view returns (uint256);

  function virtualBalanceForUtilization() external view returns (uint256);

  function virtualBalanceForUtilization(
    uint256 extraAmount,
    int256 unrealizedFunding
  ) external view returns (uint256);

  function underlyingBalanceForExchangeRate() external view returns (uint256);

  function sendAssetToTrader(address to, uint256 amount) external;

  function isUtilizationForLPsValid() external view returns (bool);
}

interface ILexPoolV1 is ILexPoolFunctionality {
  function name() external view returns (string memory);

  function symbol() external view returns (string memory);

  function SELF_UNIT_SCALE() external view returns (uint);

  function underlyingDecimals() external view returns (uint256);

  function poolAccountant() external view returns (address);

  function underlying() external view returns (IERC20);

  function tradingFloor() external view returns (address);

  function currentEpoch() external view returns (uint256);

  function currentExchangeRate() external view returns (uint256);

  function nextEpochStartMin() external view returns (uint256);

  function epochDuration() external view returns (uint256);

  function minDepositAmount() external view returns (uint256);

  function epochsDelayDeposit() external view returns (uint256);

  function epochsDelayRedeem() external view returns (uint256);

  function immediateDepositAllowed() external view returns (bool);

  function pendingDeposits(
    uint epoch,
    address account
  ) external view returns (PendingDeposit memory);

  function pendingRedeems(
    uint epoch,
    address account
  ) external view returns (PendingRedeem memory);

  function pendingDepositAmount() external view returns (uint256);

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

File 12 of 20 : ILynxVersionedContract.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;

interface ILynxVersionedContract {
  /**
   * @notice Returns the name of the contract
   */
  function getContractName() external view returns (string memory);

  /**
   * @notice Returns the version of the contract
   * @dev units are scaled by 1000 (1,000 = 1.00, 1,120 = 1.12)
   */
  function getContractVersion() external view returns (string memory);
}

File 13 of 20 : IPoolAccountantV1.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;

import "./LexErrors.sol";
import "./ILexPoolV1.sol";
import "./IInterestRateModel.sol";
import "./IFundingRateModel.sol";
import "./TradingEnumsV1.sol";

interface PoolAccountantStructs {
  // @note To be used for passing information in function calls
  struct PositionRegistrationParams {
    uint256 collateral;
    uint32 leverage;
    bool long;
    uint64 openPrice;
    uint64 tp;
  }

  struct PairFunding {
    // Slot 0
    int256 accPerOiLong; // 32 bytes -- Underlying Decimals
    // Slot 1
    int256 accPerOiShort; // 32 bytes -- Underlying Decimals
    // Slot 2
    uint256 lastUpdateTimestamp; // 32 bytes
  }

  struct TradeInitialAccFees {
    // Slot 0
    uint256 borrowIndex; // 32 bytes
    // Slot 1
    int256 funding; // 32 bytes -- underlying units -- Underlying Decimals
  }

  struct PairOpenInterest {
    // Slot 0
    uint256 long; // 32 bytes -- underlying units -- Dynamic open interest for long positions
    // Slot 1
    uint256 short; // 32 bytes -- underlying units -- Dynamic open interest for short positions
  }

  // This struct is not kept in storage
  struct PairFromTo {
    string from;
    string to;
  }

  struct Pair {
    // Slot 0
    uint16 id; // 02 bytes
    uint16 groupId; // 02 bytes
    uint16 feeId; // 02 bytes
    uint32 minLeverage; // 04 bytes
    uint32 maxLeverage; // 04 bytes
    uint32 maxBorrowF; // 04 bytes -- FRACTION_SCALE (5)
    // Slot 1
    uint256 maxPositionSize; // 32 bytes -- underlying units
    // Slot 2
    uint256 maxGain; // 32 bytes -- underlying units
    // Slot 3
    uint256 maxOpenInterest; // 32 bytes -- Underlying units
    // Slot 4
    uint256 maxSkew; // 32 bytes -- underlying units
    // Slot 5
    uint256 minOpenFee; // 32 bytes -- underlying units. MAX_UINT means use the default group level value
    // Slot 6
    uint256 minPerformanceFee; // 32 bytes -- underlying units
  }

  struct Group {
    // Slot 0
    uint16 id; // 02 bytes
    uint32 minLeverage; // 04 bytes
    uint32 maxLeverage; // 04 bytes
    uint32 maxBorrowF; // 04 bytes -- FRACTION_SCALE (5)
    // Slot 1
    uint256 maxPositionSize; // 32 bytes (Underlying units)
    // Slot 2
    uint256 minOpenFee; // 32 bytes (Underlying uints). MAX_UINT means use the default global level value
  }

  struct Fee {
    // Slot 0
    uint16 id; // 02 bytes
    uint32 openFeeF; // 04 bytes -- FRACTION_SCALE (5) (Fraction of leveraged pos)
    uint32 closeFeeF; // 04 bytes -- FRACTION_SCALE (5) (Fraction of leveraged pos)
    uint32 performanceFeeF; // 04 bytes -- FRACTION_SCALE (5) (Fraction of performance)
  }
}

interface PoolAccountantEvents is PoolAccountantStructs {
  event PairAdded(
    uint256 indexed id,
    string indexed from,
    string indexed to,
    Pair pair
  );
  event PairUpdated(uint256 indexed id, Pair pair);

  event GroupAdded(uint256 indexed id, string indexed groupName, Group group);
  event GroupUpdated(uint256 indexed id, Group group);

  event FeeAdded(uint256 indexed id, string indexed name, Fee fee);
  event FeeUpdated(uint256 indexed id, Fee fee);

  event TradeInitialAccFeesStored(
    bytes32 indexed positionId,
    uint256 borrowIndex,
    // uint256 rollover,
    int256 funding
  );

  event AccrueFunding(
    uint256 indexed pairId,
    int256 valueLong,
    int256 valueShort
  );

  event ProtocolFundingShareAccrued(
    uint16 indexed pairId,
    uint256 protocolFundingShare
  );
  // event AccRolloverFeesStored(uint256 pairIndex, uint256 value);

  event FeesCharged(
    bytes32 indexed positionId,
    address indexed trader,
    uint16 indexed pairId,
    PositionRegistrationParams positionRegistrationParams,
    //        bool long,
    //        uint256 collateral, // Underlying Decimals
    //        uint256 leverage,
    int256 profitPrecision, // PRECISION
    uint256 interest,
    int256 funding, // Underlying Decimals
    uint256 closingFee,
    uint256 tradeValue
  );

  event PerformanceFeeCharging(
    bytes32 indexed positionId,
    uint256 performanceFee
  );

  event MaxOpenInterestUpdated(uint256 pairIndex, uint256 maxOpenInterest);

  event AccrueInterest(
    uint256 cash,
    uint256 totalInterestNew,
    uint256 borrowIndexNew,
    uint256 interestShareNew
  );

  event Borrow(
    uint256 indexed pairId,
    uint256 borrowAmount,
    uint256 newTotalBorrows
  );

  event Repay(
    uint256 indexed pairId,
    uint256 repayAmount,
    uint256 newTotalBorrows
  );
}

interface IPoolAccountantFunctionality is
  PoolAccountantStructs,
  PoolAccountantEvents,
  LexErrors,
  TradingEnumsV1
{
  function setTradeIncentivizer(address _tradeIncentivizer) external;

  function setMaxGainF(uint256 _maxGainF) external;

  function setFrm(IFundingRateModel _frm) external;

  function setMinOpenFee(uint256 min) external;

  function setLexPartF(uint256 partF) external;

  function setFundingRateMax(uint256 maxValue) external;

  function setLiquidationThresholdF(uint256 threshold) external;

  function setLiquidationFeeF(uint256 fee) external;

  function setIrm(IInterestRateModel _irm) external;

  function setIrmHard(IInterestRateModel _irm) external;

  function setInterestShareFactor(uint256 factor) external;
  
  function setFundingShareFactor(uint256 factor) external;

  function setBorrowRateMax(uint256 rate) external;

  function setMaxTotalBorrows(uint256 maxBorrows) external;

  function setMaxVirtualUtilization(uint256 _maxVirtualUtilization) external;

  function resetTradersPairGains(uint256 pairId) external;

  function addGroup(Group calldata _group) external;

  function updateGroup(Group calldata _group) external;

  function addFee(Fee calldata _fee) external;

  function updateFee(Fee calldata _fee) external;

  function addPair(Pair calldata _pair) external;

  function addPairs(Pair[] calldata _pairs) external;

  function updatePair(Pair calldata _pair) external;

  function readAndZeroReserves()
    external
    returns (uint256 accumulatedInterestShare,
             uint256 accFundingShare);

  function registerOpenTrade(
    bytes32 positionId,
    address trader,
    uint16 pairId,
    uint256 collateral,
    uint32 leverage,
    bool long,
    uint256 tp,
    uint256 openPrice
  ) external returns (uint256 fee, uint256 lexPartFee);

  function registerCloseTrade(
    bytes32 positionId,
    address trader,
    uint16 pairId,
    PositionRegistrationParams calldata positionRegistrationParams,
    uint256 closePrice,
    PositionCloseType positionCloseType
  )
    external
    returns (
      uint256 closingFee,
      uint256 tradeValue,
      int256 profitPrecision,
      uint finalClosePrice
    );

  function registerUpdateTp(
    bytes32 positionId,
    address trader,
    uint16 pairId,
    uint256 collateral,
    uint32 leverage,
    bool long,
    uint256 openPrice,
    uint256 oldTriggerPrice,
    uint256 triggerPrice
  ) external;

  // function registerUpdateSl(
  //     address trader,
  //     uint256 pairIndex,
  //     uint256 index,
  //     uint256 collateral,
  //     uint256 leverage,
  //     bool long,
  //     uint256 openPrice,
  //     uint256 triggerPrice
  // ) external returns (uint256 fee);

  function accrueInterest()
    external
    returns (
      uint256 totalInterestNew,
      uint256 interestShareNew,
      uint256 borrowIndexNew
    );

  // Limited only for the LexPool
  function accrueInterest(
    uint256 availableCash
  )
    external
    returns (
      uint256 totalInterestNew,
      uint256 interestShareNew,
      uint256 borrowIndexNew
    );

  function getTradeClosingValues(
    bytes32 positionId,
    uint16 pairId,
    PositionRegistrationParams calldata positionRegistrationParams,
    uint256 closePrice,
    bool isLiquidation
  )
    external
    returns (
      uint256 tradeValue, // Underlying Decimals
      uint256 safeClosingFee,
      int256 profitPrecision,
      uint256 interest,
      int256 funding
    );

  function getTradeLiquidationPrice(
    bytes32 positionId,
    uint16 pairId,
    uint256 openPrice, // PRICE_SCALE (8)
    uint256 tp,
    bool long,
    uint256 collateral, // Underlying Decimals
    uint32 leverage
  )
    external
    returns (
      uint256 // PRICE_SCALE (8)
    );

  function calcTradeDynamicFees(
    bytes32 positionId,
    uint16 pairId,
    bool long,
    uint256 collateral,
    uint32 leverage,
    uint256 openPrice,
    uint256 tp
  ) external returns (uint256 interest, int256 funding);

  function unrealizedFunding() external view returns (int256);

  function totalBorrows() external view returns (uint256);

  function interestShare() external view returns (uint256);
  
  function fundingShare() external view returns (uint256);

  function totalReservesView() external view returns (uint256);

  function borrowsAndInterestShare()
    external
    view
    returns (uint256 totalBorrows, uint256 totalInterestShare);

  function pairTotalOpenInterest(
    uint256 pairIndex
  ) external view returns (int256);

  function pricePnL(
    uint256 pairId,
    uint256 price
  ) external view returns (int256);

  function getAllSupportedPairIds() external view returns (uint16[] memory);

  function getAllSupportedGroupsIds() external view returns (uint16[] memory);

  function getAllSupportedFeeIds() external view returns (uint16[] memory);
}

interface IPoolAccountantV1 is IPoolAccountantFunctionality {
  function totalBorrows() external view returns (uint256);

  function maxTotalBorrows() external view returns (uint256);

  function pairBorrows(uint256 pairId) external view returns (uint256);

  function groupBorrows(uint256 groupId) external view returns (uint256);

  function pairMaxBorrow(uint16 pairId) external view returns (uint256);

  function groupMaxBorrow(uint16 groupId) external view returns (uint256);

  function lexPool() external view returns (ILexPoolV1);

  function maxGainF() external view returns (uint256);

  function interestShareFactor() external view returns (uint256);
  
  function fundingShareFactor() external view returns (uint256);

  function frm() external view returns (IFundingRateModel);

  function irm() external view returns (IInterestRateModel);

  function pairs(uint16 pairId) external view returns (Pair memory);

  function groups(uint16 groupId) external view returns (Group memory);

  function fees(uint16 feeId) external view returns (Fee memory);

  function openInterestInPair(
    uint pairId
  ) external view returns (PairOpenInterest memory);

  function minOpenFee() external view returns (uint256);

  function liquidationThresholdF() external view returns (uint256);

  function liquidationFeeF() external view returns (uint256);

  function lexPartF() external view returns (uint256);

  function tradersPairGains(uint256 pairId) external view returns (int256);

  function calcBorrowAmount(
    uint256 collateral,
    uint256 leverage,
    bool long,
    uint256 openPrice,
    uint256 tp
  ) external pure returns (uint256);
}

File 14 of 20 : LexErrors.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;

interface LexErrors {
  enum CapType {
    NONE, // 0
    MIN_OPEN_FEE, // 1
    MAX_POS_SIZE_PAIR, // 2
    MAX_POS_SIZE_GROUP, // 3
    MAX_LEVERAGE, // 4
    MIN_LEVERAGE, // 5
    MAX_VIRTUAL_UTILIZATION, // 6
    MAX_OPEN_INTEREST, // 7
    MAX_ABS_SKEW, // 8
    MAX_BORROW_PAIR, // 9
    MAX_BORROW_GROUP, // 10
    MIN_DEPOSIT_AMOUNT, // 11
    MAX_ACCUMULATED_GAINS, // 12
    BORROW_RATE_MAX, // 13
    FUNDING_RATE_MAX, // 14
    MAX_POTENTIAL_GAIN, // 15
    MAX_TOTAL_BORROW, // 16
    MIN_PERFORMANCE_FEE // 17
    //...
  }
  error CapError(CapType, uint256 value);
}

File 15 of 20 : LexPoolAdminEnums.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;

interface LexPoolAdminEnums {
  enum LexPoolAddressesEnum {
    none,
    poolAccountant,
    pnlRole
  }

  enum LexPoolNumbersEnum {
    none,
    maxExtraWithdrawalAmountF,
    epochsDelayDeposit,
    epochsDelayRedeem,
    epochDuration,
    minDepositAmount
  }
}

File 16 of 20 : TradingEnumsV1.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;

interface TradingEnumsV1 {
  enum PositionPhase {
    NONE,
    OPEN_MARKET,
    OPEN_LIMIT,
    OPENED,
    CLOSE_MARKET,
    CLOSED
  }

  enum OpenOrderType {
    NONE,
    MARKET,
    LIMIT
  }
  enum CloseOrderType {
    NONE,
    MARKET
  }
  enum FeeType {
    NONE,
    OPEN_FEE,
    CLOSE_FEE,
    TRIGGER_FEE
  }
  enum LimitTrigger {
    NONE,
    TP,
    SL,
    LIQ
  }
  enum PositionField {
    NONE,
    TP,
    SL
  }

  enum PositionCloseType {
    NONE,
    TP,
    SL,
    LIQ,
    MARKET
  }
}

File 17 of 20 : AbstractPullSourceV1.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;

import {IDispenserPullSourceV1} from "../interfaces/IDispenserPullSourceV1.sol";
import {TokenHandlingContractUtils} from "../../../Utils/TokenHandlingContractUtils.sol";

abstract contract AbstractPullSourceV1 is
  TokenHandlingContractUtils,
  IDispenserPullSourceV1
{
  // ***** Events *****

  event DownstreamDispenserSet(
    address indexed previousDownstreamDispenser,
    address indexed newDownstreamDispenser
  );
  event TokenDispensed(
    address indexed token,
    address indexed account,
    address indexed receiver,
    uint256 amount
  );
  event TokenCredited(
    address indexed token,
    address indexed account,
    uint256 amountCredited
  );
  event TokenDiscredited(
    address indexed token,
    address indexed account,
    uint256 amountCredited
  );

  // ***** Storage *****

  address public downstreamDispenser;

  // @dev : This is the total amount of tokens that are pending to be distributed.
  // token => total pending distributions
  mapping(address => uint256) public totalPendingDispensations;

  // token => account => pending amount
  mapping(address => mapping(address => uint256))
    public pendingDispensationsForAccount;

  // ***** Modifiers *****

  modifier onlyDownstreamDispenser() {
    require(msg.sender == downstreamDispenser, "NOT_DOWNSTREAM_DISPENSER");
    _;
  }

  // ***** Views *****

  /**
   * @notice Returns whether the dispenser is matching the source.
   * @return True if interface matching
   */
  function isDispenserMatchingSourceV1() external pure override returns (bool) {
    return true;
  }

  /**
   * @notice Returns the pending amount for the account if it were to be pulled now without any state.
   * @param _token The token to get the pending amount for.
   * @param _account The account to get the pending amount for.
   * @return The pending amount for the account.
   */
  function getPendingAmountForAccountView(
    address _token,
    address _account
  ) public view override returns (uint256) {
    return
      pendingDispensationsForAccount[_token][_account] +
      _getExternalPendingAmountForAccountView(_token, _account);
  }

  // ***** View-Like *****

  /**
   * @notice Returns the pending amount for the account if it were to be pulled now.
   * @dev This function is not a view as it may update the state.
   * @param _token The token to get the pending amount for.
   * @param _account The account to get the pending amount for.
   * @return The pending amount for the account.
   */
  function getPendingAmountForAccount(
    address _token,
    address _account
  ) external override returns (uint256) {
    _pullFromAllSourcesAndRegisterInternal(_token, _account);
    return pendingDispensationsForAccount[_token][_account];
  }

  // ***** Constructor *****

  constructor() {}

  // ***** Downstream Dispenser Functions *****

  /**
   * @notice Requests to receive the entire amount '_account' is entitled to
   * @dev This function is expected to be called by the dispenser, the source is expected to transfer tokens to the dispenser
   *      to be credited for '_account'
   * @param _token The token to request.
   * @param _account The account to request the token for.
   */
  function pullTokenForAccount(
    address _token,
    address _account
  ) external override onlyDownstreamDispenser {
    // Sanity
    require(downstreamDispenser != address(0), "NO_DOWNSTREAM_DISPENSER");
    _pullTokenForAccountInternal(_token, _account);
  }

  /**
   * @notice Requests to receive the entire amount '_account' is entitled to
   * @dev This function is expected to be called by the dispenser, the source is expected to transfer tokens to the dispenser
   *      to be credited for '_account'
   * @param _token The token to request.
   * @param _accounts The accounts to request the token for.
   * @return amounts an array of amounts that were pulled for each account
   */
  function pullTokenForAccounts(
    address _token,
    address[] calldata _accounts
  )
    external
    override
    onlyDownstreamDispenser
    returns (uint256[] memory amounts)
  {
    // Sanity
    require(downstreamDispenser != address(0), "NO_DOWNSTREAM_DISPENSER");
    amounts = new uint256[](_accounts.length);
    for (uint256 i = 0; i < _accounts.length; i++) {
      amounts[i] = _pullTokenForAccountInternal(_token, _accounts[i]);
    }
  }

  // ***** Internal Admin Functions *****

  function setDownstreamDispenserInternal(
    address _downstreamDispenser
  ) internal {
    // Storage
    address previousDownstreamDispenser = downstreamDispenser;
    downstreamDispenser = _downstreamDispenser;

    // Event
    emit DownstreamDispenserSet(
      previousDownstreamDispenser,
      _downstreamDispenser
    );
  }

  // ***** Internal Pulling Functions *****

  /**
   * @notice Implements the behaviour expected from this contract as a 'DispensesPullSource'
   * @dev will pull from all sources and send and entitlement to 'downstreamDispenser'
   */
  function _pullTokenForAccountInternal(
    address _token,
    address _account
  ) internal returns (uint256 amountDispensedToAccount) {
    // First pull from all sources available to this contract
    _pullFromAllSourcesAndRegisterInternal(_token, _account);

    // Then, dispense to 'downstreamDispenser'
    amountDispensedToAccount = dispensePendingTokenToReceiverInternal(
      address(_token),
      _account,
      downstreamDispenser
    );
  }

  /**
   * @notice Will send all of _account pending _token to _receiver
   */
  function dispensePendingTokenToReceiverInternal(
    address _token,
    address _account,
    address _receiver
  ) internal returns (uint256 amountDispensed) {
    // Sanity
    require(_receiver != address(0), "CANNOT_DISPENSE_TO_ZERO");

    // Zero pending amount
    amountDispensed = pendingDispensationsForAccount[_token][_account];
    pendingDispensationsForAccount[_token][_account] = 0;

    totalPendingDispensations[_token] -= amountDispensed;

    // Internal hook
    onTokenDispensedToReceiverInternal(
      _token,
      _account,
      _receiver,
      amountDispensed
    );

    // Send token
    sendTokens(_token, _receiver, amountDispensed);

    // Event
    emit TokenDispensed(_token, _account, _receiver, amountDispensed);
  }

  // ***** Internal Credit/Discredit Functions *****

  /**
   * @notice Increases pending amount for account
   */
  function creditTokenInternal(
    address _token,
    address _account,
    uint256 _amount
  ) internal {
    uint256 newAccountPending = pendingDispensationsForAccount[_token][
      _account
    ] + _amount;
    uint256 newTokenPending = totalPendingDispensations[_token] + _amount;

    pendingDispensationsForAccount[_token][_account] = newAccountPending;
    totalPendingDispensations[_token] = newTokenPending;

    emit TokenCredited(_token, _account, _amount);
  }

  /**
   * @notice Reduces pending amount for account
   * @dev 0 means all. Will revert if amount is greater than pending amount
   */
  function discreditTokenInternal(
    address _token,
    address _account,
    uint256 _amount,
    address _destination
  ) internal {
    // Start by pulling from all sources
    _pullFromAllSourcesAndRegisterInternal(_token, _account);

    uint256 currentPending = pendingDispensationsForAccount[_token][_account];
    uint256 amountReduced = 0;

    if (_amount == 0) {
      pendingDispensationsForAccount[_token][_account] = 0;
      amountReduced = currentPending;
    } else {
      require(currentPending >= _amount, "AMOUNT_EXCEEDS_PENDING");
      pendingDispensationsForAccount[_token][_account] =
        currentPending -
        _amount;
      amountReduced = _amount;
    }

    // Update total pending and avoid underflow
    uint256 currentTotalPending = totalPendingDispensations[_token];
    totalPendingDispensations[_token] = amountReduced > currentTotalPending
      ? 0
      : currentTotalPending - amountReduced;

    sendTokens(_token, _destination, amountReduced);

    emit TokenDiscredited(_token, _account, amountReduced);
  }

  // ***** Internal Overridable Functions *****

  /**
   * @notice Pulls the pending amount for the account from the external sources.
   * @dev This function is virtual and can be overridden and implemented by the child contract.
   * @param _token The token to pull the pending amount for.
   * @param _account The account to pull the pending amount for.
   * @return externalPulledAmount The amount pulled.
   */
  function _pullFromAllSourcesAndRegisterInternal(
    address _token,
    address _account
  ) internal virtual returns (uint256 externalPulledAmount) {}

  /**
   * @notice Reads the pending amount for the account from the external sources.
   * @dev This function is virtual and can be overridden and implemented by the child contract.
   * @param _token The token to pull the pending amount for.
   * @param _account The account to pull the pending amount for.
   * @return externalPendingAmount The amount read.
   */
  function _getExternalPendingAmountForAccountView(
    address _token,
    address _account
  ) internal view virtual returns (uint256 externalPendingAmount) {}

  /**
   * @notice Called upon token dispensation to allow custom logic.
   * @dev This function is virtual used as a hook for inheriting contracts
   * @param _token The token to send.
   * @param _receiver The receiver to send the tokens to.
   * @param amountDispensed The amount to be send.
   */
  function onTokenDispensedToReceiverInternal(
    address _token,
    address _account,
    address _receiver,
    uint256 amountDispensed
  ) internal virtual {}
}

File 18 of 20 : IDispenserPullSourceV1.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;

interface IDispenserPullSourceV1 {
  /**
   * @notice Returns whether the dispenser is matching the source.
   * @return True if interface matching
   */
  function isDispenserMatchingSourceV1() external pure returns (bool);

  /**
   * @notice Returns the pending amount for the account if it were to be pulled now.
   * @dev This function is not a view as it may update the state.
   * @param _token The token to get the pending amount for.
   * @param _account The account to get the pending amount for.
   * @return The pending amount for the account.
   */
  function getPendingAmountForAccount(
    address _token,
    address _account
  ) external returns (uint256);

  /**
   * @notice Returns the pending amount for the account if it were to be pulled now without any state.
   * @param _token The token to get the pending amount for.
   * @param _account The account to get the pending amount for.
   * @return The pending amount for the account.
   */
  function getPendingAmountForAccountView(
    address _token,
    address _account
  ) external view returns (uint256);

  /**
   * @notice Requests to receive the entire amount '_account' is entitled to
   * @dev This function is expected to be called by the dispenser, the source is expected to transfer tokens to the dispenser
   *      to be credited for '_account'
   * @param _token The token to request.
   * @param _account The account to request the token for.
   */
  function pullTokenForAccount(address _token, address _account) external;

  /**
   * @notice Requests to receive the entire amount '_account' is entitled to
   * @dev This function is expected to be called by the dispenser, the source is expected to transfer tokens to the dispenser
   *      to be credited for '_account'
   * @param _token The token to request.
   * @param _accounts The accounts to request the token for.
   * @return amounts an array of amounts that were pulled for each account
   */
  function pullTokenForAccounts(
    address _token,
    address[] calldata _accounts
  ) external returns (uint256[] memory amounts);
}

File 19 of 20 : IPushableDispenserV1.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;

interface IPushableDispenserV1 {
  /**
   * @notice Flag for interface identification
   * @return True if interface matching
   */
  function isPushableDispenserV1() external pure returns (bool);

  /**
   * @notice Called by the Pusher, dispense should pull _amount from Pusher.
   * @dev The dispenser is expected to credit the account with the tokens pulled from sender
   * @param dispensationKey The dispensation key.
   * @param dispensationRoundNumber The dispensation round number, if valid.
   * @param _token The token to push.
   * @param _account The account to push the token for.
   * @param _amount The amount to push.
   */
  function receiveDispensationPushForAccount(
    string calldata dispensationKey,
    uint256 dispensationRoundNumber,
    address _token,
    address _account,
    uint256 _amount
  ) external;

  /**
   * @notice Called by the Pusher, dispense should pull SUM(_amounts) from Pusher.
   * @dev The dispenser is expected to credit the accounts with the tokens pulled from sender
   * @param dispensationKey The dispensation key.
   * @param dispensationRoundNumber The dispensation round number, if valid.
   * @param _token The token to push.
   * @param _accounts The accounts to push the token for.
   * @param _amounts The amounts to push per account.
   */
  function receiveDispensationPushForAccounts(
    string calldata dispensationKey,
    uint256 dispensationRoundNumber,
    address _token,
    address[] calldata _accounts,
    uint256[] calldata _amounts
  ) external;
}

File 20 of 20 : TokenHandlingContractUtils.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

/**
 * @title TokenHandlingContractUtils
 * @notice Used to handle token related operations.
 * @dev DO NOT ADD STORAGE TO THIS CONTRACT
 */
contract TokenHandlingContractUtils {
  using SafeERC20 for IERC20;

  // ***** Events *****

  event TokensSwept(
    address indexed token,
    address indexed receiver,
    uint256 amount
  );

  // ***** Internal Util Functions *****

  /**
   * @notice Sweep any tokens from the contract
   * @param _token The token to sweep
   * @param _amount The amount to sweep
   */
  function sweepTokensInternal(
    address _token,
    address _receiver,
    uint256 _amount
  ) internal {
    uint256 amount = _amount > 0
      ? _amount
      : getSelfBalanceInTokenInternal(_token);

    sendTokens(_token, _receiver, amount);

    emit TokensSwept(address(_token), _receiver, amount);
  }

  /**
   * Utility function to safely take tokens (ERC20) from a pre-approved account
   * @dev Will revert if the contract will not get the exact 'amount' value
   */
  function takeTokens(address _token, address _from, uint256 _amount) internal {
    uint256 balanceBefore = getSelfBalanceInTokenInternal(_token);
    IERC20(_token).safeTransferFrom(_from, address(this), _amount);
    uint256 balanceAfter = getSelfBalanceInTokenInternal(_token);
    require(balanceAfter - balanceBefore == _amount, "DID_NOT_RECEIVE_EXACT");
  }

  /**
   * Utility function to safely send tokens (ERC20)
   */
  function sendTokens(address _token, address _to, uint256 _amount) internal {
    IERC20(_token).safeTransfer(_to, _amount);
  }

  function getSelfBalanceInTokenInternal(
    address _token
  ) internal view returns (uint256) {
    return IERC20(_token).balanceOf(address(this));
  }
}

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

Contract Security Audit

Contract ABI

[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"string","name":"dispensationKey","type":"string"},{"indexed":true,"internalType":"address","name":"token","type":"address"}],"name":"DispensationEnded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"string","name":"dispensationKey","type":"string"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"string","name":"name","type":"string"},{"indexed":false,"internalType":"string","name":"description","type":"string"}],"name":"DispensationExplained","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"string","name":"dispensationKey","type":"string"},{"indexed":true,"internalType":"address","name":"token","type":"address"}],"name":"DispensationStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousDownstreamDispenser","type":"address"},{"indexed":true,"internalType":"address","name":"newDownstreamDispenser","type":"address"}],"name":"DownstreamDispenserSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bool","name":"value","type":"bool"}],"name":"IsAllClaimingPausedFlagSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"bool","name":"value","type":"bool"}],"name":"IsClaimingPausedForTokenFlagSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"address","name":"pullSource","type":"address"}],"name":"PullSourceAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"address","name":"pullSource","type":"address"}],"name":"PullSourceRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_token","type":"address"},{"indexed":true,"internalType":"address","name":"_pusher","type":"address"},{"indexed":false,"internalType":"enum GeneralTokenDispenserV1.DispensationPushLevel","name":"level","type":"uint8"}],"name":"PusherRegistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_token","type":"address"},{"indexed":true,"internalType":"address","name":"_pusher","type":"address"}],"name":"PusherUnregistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"}],"name":"TokenAddedToCurrentlyDispensed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"}],"name":"TokenAddedToEverDispensed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountCredited","type":"uint256"}],"name":"TokenCredited","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountCredited","type":"uint256"}],"name":"TokenDiscredited","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TokenDispensed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"source","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TokenPulledForAccountFromSource","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"string","name":"dispensationKey","type":"string"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"address","name":"pusher","type":"address"},{"indexed":false,"internalType":"uint256","name":"round","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TokenPushedForAccount","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"}],"name":"TokenRemovedFromCurrentlyDispensed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TokensSwept","type":"event"},{"inputs":[],"name":"CONTRACT_NAME","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"CONTRACT_VERSION","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"address","name":"_pullSource","type":"address"}],"name":"addPullSource","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"allOngoingDispensationsForToken","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"allTokensCurrentlyDispensed","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"allTokensEverDispensed","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"address","name":"_account","type":"address"}],"name":"claimTokenFor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"address","name":"_account","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"address","name":"_destination","type":"address"}],"name":"discreditAccount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"address[]","name":"_accounts","type":"address[]"},{"internalType":"uint256[]","name":"_amounts","type":"uint256[]"},{"internalType":"address","name":"_destination","type":"address"}],"name":"discreditAccounts","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"dispensationDescriptions","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"dispensationNames","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"dispensationPusherLevels","outputs":[{"internalType":"enum GeneralTokenDispenserV1.DispensationPushLevel","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"downstreamDispensationsForAccount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"downstreamDispenser","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"_dispensationKey","type":"string"},{"internalType":"address","name":"_token","type":"address"}],"name":"endDispensation","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_dispensationKey","type":"string"},{"internalType":"address","name":"_token","type":"address"},{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_description","type":"string"}],"name":"explainDispensation","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"getAllOngoingDispensationsForToken","outputs":[{"internalType":"string[]","name":"","type":"string[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAllTokensCurrentlyDispensed","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAllTokensEverDispensed","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getContractName","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getContractVersion","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"dispensationKey","type":"string"},{"internalType":"address","name":"_token","type":"address"}],"name":"getDispensationInfo","outputs":[{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"description","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"address","name":"_pusher","type":"address"}],"name":"getDispensationPusherLevel","outputs":[{"internalType":"enum GeneralTokenDispenserV1.DispensationPushLevel","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"address","name":"_account","type":"address"}],"name":"getPendingAmountForAccount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"address","name":"_account","type":"address"}],"name":"getPendingAmountForAccountView","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"getPullSourcesForToken","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"getPushersForToken","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"historicDispensationsForAccount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isAllClaimingPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isClaimingPausedForToken","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isDispenserMatchingSourceV1","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"isOngoingDispensation","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"isPullSourceForToken","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isPushableDispenserV1","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isTokenCurrentlyDispensed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"pendingDispensationsForAccount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"address","name":"_account","type":"address"}],"name":"pullAllAndClaimFor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"address","name":"_account","type":"address"},{"internalType":"address[]","name":"pullSources","type":"address[]"}],"name":"pullSomeAndClaimFor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"pullSourcesForToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"address","name":"_account","type":"address"}],"name":"pullTokenForAccount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"address[]","name":"_accounts","type":"address[]"}],"name":"pullTokenForAccounts","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"pushersForToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"_dispensationKey","type":"string"},{"internalType":"uint256","name":"_dispensationRoundNumber","type":"uint256"},{"internalType":"address","name":"_token","type":"address"},{"internalType":"address","name":"_account","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"receiveDispensationPushForAccount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_dispensationKey","type":"string"},{"internalType":"uint256","name":"_dispensationRoundNumber","type":"uint256"},{"internalType":"address","name":"_token","type":"address"},{"internalType":"address[]","name":"_accounts","type":"address[]"},{"internalType":"uint256[]","name":"_amounts","type":"uint256[]"}],"name":"receiveDispensationPushForAccounts","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"address","name":"_pusher","type":"address"},{"internalType":"enum GeneralTokenDispenserV1.DispensationPushLevel","name":"level","type":"uint8"}],"name":"registerPusher","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"address","name":"_pullSource","type":"address"}],"name":"removePullSource","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_downstreamDispenser","type":"address"}],"name":"setDownstreamDispenser","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_value","type":"bool"}],"name":"setIsAllClaimingPaused","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"bool","name":"_value","type":"bool"}],"name":"setIsClaimingPausedForToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_dispensationKey","type":"string"},{"internalType":"address","name":"_token","type":"address"},{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_description","type":"string"}],"name":"startDispensation","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"sweepTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"totalPendingDispensations","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"address","name":"_pusher","type":"address"}],"name":"unregisterPusher","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"wasTokenEverDispensed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"}]

60806040523480156200001157600080fd5b5033806200003957604051631e4fbdf760e01b81526000600482015260240160405180910390fd5b62000044816200004b565b50620000b9565b600180546001600160a01b0319169055620000668162000069565b50565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6143a080620000c96000396000f3fe608060405234801561001057600080fd5b50600436106103975760003560e01c806379ba5097116101e9578063b7b6287e1161010f578063e30c3978116100ad578063f3695d981161007c578063f3695d98146108c8578063f44978f914610949578063f5f5ba721461095c578063ffb85f5a1461098f57600080fd5b8063e30c3978146108e2578063e50c26b9146108f3578063e9597a8b14610916578063f2fde38b1461093657600080fd5b8063b987b2e8116100e9578063b987b2e814610887578063cc236cc7146108b5578063d1a8ff54146108c8578063dec66036146108cf57600080fd5b8063b7b6287e14610859578063b836441e1461086c578063b8b65cf41461087457600080fd5b80638da5cb5b116101875780639d8ec144116101565780639d8ec144146107e8578063a8bc01a4146107fb578063a9b8c5b41461081b578063b058fa361461084657600080fd5b80638da5cb5b146107a45780638e681ec3146107b55780638e96317f146107c85780639553c578146107db57600080fd5b806386725a75116101c357806386725a7514610720578063874951c3146107335780638aa10435146107725780638bf4d4af1461079157600080fd5b806379ba5097146106f257806379c12917146106fa57806385f9db671461070d57600080fd5b8063330ca298116102ce5780635b2e448f1161026c5780636a274d221161023b5780636a274d221461068c578063715018a6146106b757806374b3ff4f146106bf5780637820c216146106df57600080fd5b80635b2e448f14610602578063614d08f814610615578063647aefbd1461064b578063677625a71461067957600080fd5b806342456d0d116102a857806342456d0d146105805780634b98684f146105935780634eb4164c146105a657806352fc7984146105b957600080fd5b8063330ca2981461051d57806336fa5f081461053e57806338b903331461055157600080fd5b80631a6748f41161033b57806323e86e341161031557806323e86e34146104c1578063247858ba146104d45780632d7ce37f146104e75780632eb37ff4146104fa57600080fd5b80631a6748f414610470578063213e832814610483578063231d89c8146104ae57600080fd5b80630dd7b0cb116103775780630dd7b0cb146104225780630e6adf37146104355780630eceebc51461044a57806311bb93511461045d57600080fd5b80626dff4b1461039c57806272333d146103d4578063099b58fd146103e9575b600080fd5b6103bf6103aa36600461366f565b600d6020526000908152604090205460ff1681565b60405190151581526020015b60405180910390f35b6103e76103e236600461368a565b6109a2565b005b6104146103f736600461368a565b600f60209081526000928352604080842090915290825290205481565b6040519081526020016103cb565b6103e761043036600461368a565b610a5d565b61043d610ac5565b6040516103cb91906136bd565b6103e7610458366004613755565b610b27565b61043d61046b36600461366f565b610be5565b6103e761047e36600461368a565b610c5b565b6104966104913660046137b5565b610e10565b6040516001600160a01b0390911681526020016103cb565b61043d6104bc36600461366f565b610e3a565b6103e76104cf36600461380f565b610eae565b600254610496906001600160a01b031681565b6104146104f536600461368a565b610f35565b6103bf61050836600461366f565b60066020526000908152604090205460ff1681565b61053061052b3660046138bc565b610f70565b6040516103cb92919061395f565b61049661054c36600461398d565b611113565b6105736040518060400160405280600381526020016203130360ec1b81525081565b6040516103cb91906139b7565b6103e761058e36600461368a565b61114b565b6103e76105a13660046139ca565b6111be565b6103e76105b4366004613a15565b61135b565b6105f56105c736600461368a565b6001600160a01b039182166000908152600a6020908152604080832093909416825291909152205460ff1690565b6040516103cb9190613a78565b6103e761061036600461368a565b611375565b6105736040518060400160405280601781526020017647656e6572616c546f6b656e44697370656e736572563160481b81525081565b6103bf61065936600461368a565b600860209081526000928352604080842090915290825290205460ff1681565b6103e7610687366004613aae565b611559565b61041461069a36600461368a565b601060209081526000928352604080842090915290825290205481565b6103e76115ee565b6106d26106cd366004613acb565b611602565b6040516103cb9190613b1d565b6104966106ed36600461398d565b611758565b6103e7611774565b61041461070836600461368a565b6117b8565b6103e761071b366004613b55565b6117fb565b6103e761072e36600461366f565b6118a1565b6103bf610741366004613bfa565b6012602090815260009283526040909220815180830184018051928152908401929093019190912091525460ff1681565b60408051808201909152600381526203130360ec1b6020820152610573565b61057361079f366004613bfa565b6118b2565b6000546001600160a01b0316610496565b6103e76107c336600461380f565b611968565b6103e76107d63660046138bc565b611b32565b6005546103bf9060ff1681565b6105736107f636600461398d565b611d77565b61080e61080936600461366f565b611daf565b6040516103cb9190613cbb565b61041461082936600461368a565b600460209081526000928352604080842090915290825290205481565b6103e7610854366004613d1f565b611e9e565b6104966108673660046137b5565b611f75565b61043d611f85565b610573610882366004613bfa565b611fe5565b6105f561089536600461368a565b600a60209081526000928352604080842090915290825290205460ff1681565b6103e76108c3366004613d94565b61201a565b60016103bf565b6103e76108dd36600461398d565b6121cb565b6001546001600160a01b0316610496565b6103bf61090136600461366f565b600e6020526000908152604090205460ff1681565b61041461092436600461366f565b60036020526000908152604090205481565b6103e761094436600461366f565b6121f3565b6103e7610957366004613e4a565b612264565b60408051808201909152601781527647656e6572616c546f6b656e44697370656e736572563160481b6020820152610573565b6103e761099d36600461368a565b612324565b6002546001600160a01b031633146109fc5760405162461bcd60e51b81526020600482015260186024820152772727aa2fa227aba729aa2922a0a6afa224a9a822a729a2a960411b60448201526064015b60405180910390fd5b6002546001600160a01b0316610a4e5760405162461bcd60e51b81526020600482015260176024820152762727afa227aba729aa2922a0a6afa224a9a822a729a2a960491b60448201526064016109f3565b610a588282612526565b505050565b600554829060ff1615610a825760405162461bcd60e51b81526004016109f390613e81565b6001600160a01b03811660009081526006602052604090205460ff1615610abb5760405162461bcd60e51b81526004016109f390613eae565b610a58838361254c565b6060600c805480602002602001604051908101604052809291908181526020018280548015610b1d57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610aff575b5050505050905090565b600554849060ff1615610b4c5760405162461bcd60e51b81526004016109f390613e81565b6001600160a01b03811660009081526006602052604090205460ff1615610b855760405162461bcd60e51b81526004016109f390613eae565b81610bc85760405162461bcd60e51b81526020600482015260136024820152721393d7d4d3d55490d154d7d41493d592511151606a1b60448201526064016109f3565b610bd483838787612557565b610bde858561254c565b5050505050565b6001600160a01b038116600090815260096020908152604091829020805483518184028101840190945280845260609392830182828015610c4f57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610c31575b50505050509050919050565b610c6361259c565b610c6d82826125c9565b610c895760405162461bcd60e51b81526004016109f390613ee5565b6001600160a01b038281166000818152600a602090815260408083209486168352938152838220805460ff191690559181526009909152908120905b8154811015610dc157826001600160a01b0316828281548110610cea57610cea613f14565b6000918252602090912001546001600160a01b031603610db95781548290610d1490600190613f40565b81548110610d2457610d24613f14565b9060005260206000200160009054906101000a90046001600160a01b0316828281548110610d5457610d54613f14565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555081805480610d9257610d92613f53565b600082815260209020810160001990810180546001600160a01b0319169055019055610dc1565b600101610cc5565b50610dcb83612611565b816001600160a01b0316836001600160a01b03167f5144cb6540b0046ea54d1878787028f5635d75813e2aaf25ea0904fc69f47e3360405160405180910390a3505050565b600b8181548110610e2057600080fd5b6000918252602090912001546001600160a01b0316905081565b6001600160a01b038116600090815260076020908152604091829020805483518184028101840190945280845260609392830182828015610c4f576020028201919060005260206000209081546001600160a01b03168152600190910190602001808311610c315750505050509050919050565b610eb661259c565b610f2c87878787878080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050604080516020601f8b01819004810282018101909252898152925089915088908190840183828082843760009201919091525061279192505050565b50505050505050565b6000610f418383612881565b50506001600160a01b038083166000908152600460209081526040808320938516835292905220545b92915050565b6001600160a01b0381166000908152601360205260409081902090516060918291610f9e9087908790613f69565b908152602001604051809103902060146000856001600160a01b03166001600160a01b031681526020019081526020016000208686604051610fe1929190613f69565b9081526020016040518091039020818054610ffb90613f79565b80601f016020809104026020016040519081016040528092919081815260200182805461102790613f79565b80156110745780601f1061104957610100808354040283529160200191611074565b820191906000526020600020905b81548152906001019060200180831161105757829003601f168201915b5050505050915080805461108790613f79565b80601f01602080910402602001604051908101604052809291908181526020018280546110b390613f79565b80156111005780601f106110d557610100808354040283529160200191611100565b820191906000526020600020905b8154815290600101906020018083116110e357829003601f168201915b5050505050905091509150935093915050565b6007602052816000526040600020818154811061112f57600080fd5b6000918252602090912001546001600160a01b03169150829050565b600554829060ff16156111705760405162461bcd60e51b81526004016109f390613e81565b6001600160a01b03811660009081526006602052604090205460ff16156111a95760405162461bcd60e51b81526004016109f390613eae565b6111b38383612881565b50610a58838361254c565b6111c661259c565b6111d083836125c9565b1561121d5760405162461bcd60e51b815260206004820152601960248201527f5055534845525f414c52454144595f524547495354455245440000000000000060448201526064016109f3565b600081600181111561123157611231613a62565b0361127e5760405162461bcd60e51b815260206004820152601a60248201527f43414e4e4f545f52454749535445525f4e4f4e455f4c4556454c00000000000060448201526064016109f3565b6001600160a01b038084166000908152600a60209081526040808320938616835292905220805482919060ff1916600183818111156112bf576112bf613a62565b02179055506001600160a01b0383811660009081526009602090815260408220805460018101825590835291200180546001600160a01b03191691841691909117905561130b8361293f565b816001600160a01b0316836001600160a01b03167f196fa3ab7946db556eb94c3423dc51d780f3a397bcaecd284b44456dc88324d98360405161134e9190613a78565b60405180910390a3505050565b61136361259c565b61136f84848484612aa7565b50505050565b61137d61259c565b6001600160a01b0382166000908152600760205260408120815b815481101561149757836001600160a01b03168282815481106113bc576113bc613f14565b6000918252602090912001546001600160a01b03160361148f57815482906113e690600190613f40565b815481106113f6576113f6613f14565b9060005260206000200160009054906101000a90046001600160a01b031682828154811061142657611426613f14565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b031602179055508180548061146457611464613f53565b600082815260209020810160001990810180546001600160a01b031916905501905560019250611497565b600101611397565b50816114dd5760405162461bcd60e51b81526020600482015260156024820152741415531317d4d3d55490d157d393d517d193d55391605a1b60448201526064016109f3565b6001600160a01b038085166000908152600860209081526040808320938716835292905220805460ff1916905561151384612611565b826001600160a01b0316846001600160a01b03167f493b1bf532df7004894c240578a101f10cbb9216cc9b787dbe3cd9a81b52c4e460405160405180910390a350505050565b61156161259c565b60055481151560ff9091161515036115a95760405162461bcd60e51b815260206004820152600b60248201526a1053149150511657d4d15560aa1b60448201526064016109f3565b6005805460ff191682151590811790915560405160ff9091161515907fa042e27caefe535b8657a25f0d53a680853b51681d7d510291f72ab708b72bf090600090a250565b6115f661259c565b6116006000612c39565b565b6002546060906001600160a01b0316331461165a5760405162461bcd60e51b81526020600482015260186024820152772727aa2fa227aba729aa2922a0a6afa224a9a822a729a2a960411b60448201526064016109f3565b6002546001600160a01b03166116ac5760405162461bcd60e51b81526020600482015260176024820152762727afa227aba729aa2922a0a6afa224a9a822a729a2a960491b60448201526064016109f3565b816001600160401b038111156116c4576116c4613be4565b6040519080825280602002602001820160405280156116ed578160200160208202803683370190505b50905060005b828110156117505761172b8585858481811061171157611711613f14565b9050602002016020810190611726919061366f565b612526565b82828151811061173d5761173d613f14565b60209081029190910101526001016116f3565b509392505050565b6009602052816000526040600020818154811061112f57600080fd5b60015433906001600160a01b031681146117ac5760405163118cdaa760e01b81526001600160a01b03821660048201526024016109f3565b6117b581612c39565b50565b60006117c48383612c52565b6001600160a01b038085166000908152600460209081526040808320938716835292905220546117f49190613fb3565b9392505050565b61180361259c565b8382146118445760405162461bcd60e51b815260206004820152600f60248201526e0988a9c8ea890be9a92a69a82a8869608b1b60448201526064016109f3565b60005b84811015610f2c576118998787878481811061186557611865613f14565b905060200201602081019061187a919061366f565b86868581811061188c5761188c613f14565b9050602002013585612aa7565b600101611847565b6118a961259c565b6117b581612d74565b60146020908152600092835260409092208151808301840180519281529084019290930191909120915280546118e790613f79565b80601f016020809104026020016040519081016040528092919081815260200182805461191390613f79565b80156119605780601f1061193557610100808354040283529160200191611960565b820191906000526020600020905b81548152906001019060200180831161194357829003601f168201915b505050505081565b61197061259c565b6001600160a01b0385166000908152601260205260409081902090516119999089908990613f69565b9081526040519081900360200190205460ff16156119eb5760405162461bcd60e51b815260206004820152600f60248201526e414c52454144595f4f4e474f494e4760881b60448201526064016109f3565b6001600160a01b03851660009081526012602052604090819020905160019190611a18908a908a90613f69565b9081526040805160209281900383019020805460ff1916931515939093179092556001600160a01b0387166000908152601182529182208054600181018255908352912001611a6887898361402b565b50611adf87878787878080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050604080516020601f8b01819004810282018101909252898152925089915088908190840183828082843760009201919091525061279192505050565b846001600160a01b03168787604051611af9929190613f69565b604051908190038120907f2a9b4fc367f7e38f84011e08df7345c49bc5ab25bf5a6fa7a1c7c02f592ba8e490600090a350505050505050565b611b3a61259c565b6001600160a01b038116600090815260126020526040908190209051611b639085908590613f69565b9081526040519081900360200190205460ff16611b925760405162461bcd60e51b81526004016109f3906140e4565b6001600160a01b0381166000908152601260205260408082209051611bba9086908690613f69565b9081526040805160209281900383019020805460ff1916931515939093179092556001600160a01b03831660009081526011909152908120905b8154811015611cfb578484604051602001611c10929190613f69565b60405160208183030381529060405280519060200120828281548110611c3857611c38613f14565b90600052602060002001604051602001611c52919061411b565b6040516020818303038152906040528051906020012003611cf35781548290611c7d90600190613f40565b81548110611c8d57611c8d613f14565b90600052602060002001828281548110611ca957611ca9613f14565b906000526020600020019081611cbf9190614191565b5081805480611cd057611cd0613f53565b600190038181906000526020600020016000611cec9190613605565b9055611cfb565b600101611bf4565b50611d278484846040518060200160405280600081525060405180602001604052806000815250612791565b816001600160a01b03168484604051611d41929190613f69565b604051908190038120907fab45c658146b5490390503d9570ee0393d223a399a8b147e4a31f164e6b3dab890600090a350505050565b60116020528160005260406000208181548110611d9357600080fd5b906000526020600020016000915091505080546118e790613f79565b6001600160a01b0381166000908152601160209081526040808320805482518185028101850190935280835260609492939192909184015b82821015611e93578382906000526020600020018054611e0690613f79565b80601f0160208091040260200160405190810160405280929190818152602001828054611e3290613f79565b8015611e7f5780601f10611e5457610100808354040283529160200191611e7f565b820191906000526020600020905b815481529060010190602001808311611e6257829003601f168201915b505050505081526020019060010190611de7565b505050509050919050565b33611ea984826125c9565b611ec55760405162461bcd60e51b81526004016109f390613ee5565b6001600160a01b038416600090815260126020526040908190209051611eee9089908990613f69565b9081526040519081900360200190205460ff16611f1d5760405162461bcd60e51b81526004016109f3906140e4565b60008211611f5b5760405162461bcd60e51b815260206004820152600b60248201526a414d4f554e545f5a45524f60a81b60448201526064016109f3565b611f66843384612dc6565b610f2c87878787878787612e45565b600c8181548110610e2057600080fd5b6060600b805480602002602001604051908101604052809291908181526020018280548015610b1d576020028201919060005260206000209081546001600160a01b03168152600190910190602001808311610aff575050505050905090565b60136020908152600092835260409092208151808301840180519281529084019290930191909120915280546118e790613f79565b82811461205b5760405162461bcd60e51b815260206004820152600f60248201526e0988a9c8ea890be9a92a69a82a8869608b1b60448201526064016109f3565b3361206686826125c9565b6120825760405162461bcd60e51b81526004016109f390613ee5565b6001600160a01b0386166000908152601260205260409081902090516120ab908b908b90613f69565b9081526040519081900360200190205460ff166120da5760405162461bcd60e51b81526004016109f3906140e4565b6000805b85811015612114578484828181106120f8576120f8613f14565b905060200201358261210a9190613fb3565b91506001016120de565b50600081116121535760405162461bcd60e51b815260206004820152600b60248201526a414d4f554e545f5a45524f60a81b60448201526064016109f3565b61215e878383612dc6565b60005b858110156121be576121b68b8b8b8b8b8b8781811061218257612182613f14565b9050602002016020810190612197919061366f565b8a8a888181106121a9576121a9613f14565b9050602002013589612e45565b600101612161565b5050505050505050505050565b6121d361259c565b6121ef826121e96000546001600160a01b031690565b83612ece565b5050565b6121fb61259c565b600180546001600160a01b0383166001600160a01b0319909116811790915561222c6000546001600160a01b031690565b6001600160a01b03167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a350565b61226c61259c565b6001600160a01b03821660009081526006602052604090205481151560ff9091161515036122ca5760405162461bcd60e51b815260206004820152600b60248201526a1053149150511657d4d15560aa1b60448201526064016109f3565b6001600160a01b038216600081815260066020526040808220805460ff1916851515908117909155905160ff909116151592917fe766dd0694ff9295340d29c34dbc30b4f70c083307c3c81e5285dba3c073274991a35050565b61232c61259c565b806001600160a01b031663f3695d986040518163ffffffff1660e01b8152600401602060405180830381865afa15801561236a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061238e9190614267565b6123cf5760405162461bcd60e51b81526020600482015260126024820152714e4f545f50554c4c5f534f555243455f563160701b60448201526064016109f3565b6001600160a01b0382166000908152600760205260408120905b815481101561247857826001600160a01b031682828154811061240e5761240e613f14565b6000918252602090912001546001600160a01b0316036124705760405162461bcd60e51b815260206004820152601960248201527f50554c4c5f534f555243455f414c52454144595f41444445440000000000000060448201526064016109f3565b6001016123e9565b506001600160a01b0383811660008181526007602090815260408083208054600180820183559185528385200180546001600160a01b03191696891696871790559383526008825280832094835293905291909120805460ff191690911790556124e18361293f565b816001600160a01b0316836001600160a01b03167f46f9eb8ce96dd38714a7b9ac42bdc69a3313c60e548cf5ef032570b793045fdd60405160405180910390a3505050565b60006125328383612881565b506002546117f490849084906001600160a01b0316612f47565b610a58828283612f47565b60005b83811015610bde5761259385858381811061257757612577613f14565b905060200201602081019061258c919061366f565b8484613063565b5060010161255a565b6000546001600160a01b031633146116005760405163118cdaa760e01b81523360048201526024016109f3565b6001600160a01b038083166000908152600a6020908152604080832093851683529290529081205460ff168181600181111561260757612607613a62565b1415949350505050565b6001600160a01b03811660009081526007602090815260408083205460099092529091205481158015612642575080155b15610a585760005b600b5481101561274557836001600160a01b0316600b828154811061267157612671613f14565b6000918252602090912001546001600160a01b03160361273d57600b805461269b90600190613f40565b815481106126ab576126ab613f14565b600091825260209091200154600b80546001600160a01b0390921691839081106126d7576126d7613f14565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550600b80548061271657612716613f53565b600082815260209020810160001990810180546001600160a01b0319169055019055612745565b60010161264a565b506001600160a01b0383166000818152600e6020526040808220805460ff19169055517f5458626fa48fdcbb7a33a1f144e182222b16ecfdd862b844752fc0061c55b45a9190a2505050565b6001600160a01b0383166000908152601360205260409081902090518391906127bd9088908890613f69565b908152602001604051809103902090816127d79190614284565b506001600160a01b0383166000908152601460205260409081902090518291906128049088908890613f69565b9081526020016040518091039020908161281e9190614284565b50826001600160a01b03168585604051612839929190613f69565b60405180910390207f6580caef4438dbfe0b139727fbf172f51a284bf6e86fca731f02eea21ca58097848460405161287292919061395f565b60405180910390a35050505050565b6001600160a01b0382166000908152600760209081526040808320805482518185028101850190935280835284938301828280156128e857602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116128ca575b505050505090506000915060005b81518110156129375761292382828151811061291457612914613f14565b60200260200101518686613063565b61292d9084613fb3565b92506001016128f6565b505092915050565b6001600160a01b0381166000908152600e602052604090205460ff168015612965575050565b6001600160a01b0382166000908152600d602052604090205460ff16612a1257600c805460018082019092557fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c70180546001600160a01b0319166001600160a01b0385169081179091556000818152600d6020526040808220805460ff1916909417909355915190917ffef74848d3638a6ed3547447282ce33584cf17be953e3f9f7c6824e2284969e991a25b806121ef57600b805460018082019092557f0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01db90180546001600160a01b0319166001600160a01b0385169081179091556000818152600e6020526040808220805460ff1916909417909355915190917f74e2d6b252841daa9d389cafd5dca3a3927ef59ebfa28699c1748c1a1603250e91a25050565b612ab18484612881565b506001600160a01b03808516600090815260046020908152604080832093871683529290529081205490838103612b1157506001600160a01b03808616600090815260046020908152604080832093881683529290529081205580612b8d565b83821015612b5a5760405162461bcd60e51b8152602060048201526016602482015275414d4f554e545f455843454544535f50454e44494e4760501b60448201526064016109f3565b612b648483613f40565b6001600160a01b038088166000908152600460209081526040808320938a168352929052205550825b6001600160a01b038616600090815260036020526040902054808211612bbc57612bb78282613f40565b612bbf565b60005b6001600160a01b038816600090815260036020526040902055612be387858461307d565b856001600160a01b0316876001600160a01b03167f13268b3f271140c93c5d8b166950c19ad5320c82e78b1aa918bfb842ff13454584604051612c2891815260200190565b60405180910390a350505050505050565b600180546001600160a01b03191690556117b581613091565b6001600160a01b038216600090815260076020908152604080832080548251818502810185019093528083528493830182828015612cb957602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311612c9b575b505050505090506000915060005b815181101561293757818181518110612ce257612ce2613f14565b60209081029190910101516040516379c1291760e01b81526001600160a01b0387811660048301528681166024830152909116906379c1291790604401602060405180830381865afa158015612d3c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d609190614335565b612d6a9084613fb3565b9250600101612cc7565b600280546001600160a01b038381166001600160a01b0319831681179093556040519116919082907ff10a1acd40d60bee3d5b658d910c1f9ab77a0fe972c5de7774cd8404d65bc66990600090a35050565b6000612dd1846130e1565b9050612de86001600160a01b03851684308561314c565b6000612df3856130e1565b905082612e008383613f40565b14610bde5760405162461bcd60e51b815260206004820152601560248201527411125117d393d517d49150d152559157d1561050d5605a1b60448201526064016109f3565b612e508484846131b3565b826001600160a01b0316846001600160a01b03168888604051612e74929190613f69565b604080519182900382206001600160a01b0386168352602083018a9052908201869052907fae2504b8c277e24642789ad5fb484179bdb5a5f4dc86b63f89e21788677f3c109060600160405180910390a450505050505050565b6000808211612ee557612ee0846130e1565b612ee7565b815b9050612ef484848361307d565b826001600160a01b0316846001600160a01b03167fd092d7fceb5ea5a962639fcc27a7bb315e7637e699e3b108cd570c38c758430083604051612f3991815260200190565b60405180910390a350505050565b60006001600160a01b038216612f9f5760405162461bcd60e51b815260206004820152601760248201527f43414e4e4f545f44495350454e53455f544f5f5a45524f00000000000000000060448201526064016109f3565b506001600160a01b038084166000818152600460209081526040808320948716835293815283822080549083905592825260039052918220805491928392612fe8908490613f40565b90915550612ffa90508484848461327b565b61300584838361307d565b816001600160a01b0316836001600160a01b0316856001600160a01b03167f365c82a2bdc395fa2c986a8aa602cb7c0d691b728b80765d104b8a879b3b484f8460405161305491815260200190565b60405180910390a49392505050565b6000613070848484613318565b90506117f48383836131b3565b610a586001600160a01b038416838361346a565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6040516370a0823160e01b81523060048201526000906001600160a01b038316906370a0823190602401602060405180830381865afa158015613128573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f6a9190614335565b6040516001600160a01b03848116602483015283811660448301526064820183905261136f9186918216906323b872dd906084015b604051602081830303815290604052915060e01b6020820180516001600160e01b03838183161783525050505061349b565b6001600160a01b0380841660009081526004602090815260408083209386168352929052908120546131e6908390613fb3565b6001600160a01b0385166000908152600360205260408120549192509061320e908490613fb3565b6001600160a01b038681166000818152600460209081526040808320948a1680845294825280832088905583835260038252918290208590559051878152939450919290917fd6f1b1200b83332fb4d7e0f91e1f1672c77bb36ac9954f1b998d48f1a155434e9101612872565b816001600160a01b0316836001600160a01b0316036132d6576001600160a01b038085166000908152600f60209081526040808320938716835292905290812080548392906132cb908490613fb3565b9091555061136f9050565b6001600160a01b0380851660009081526010602090815260408083209387168352929052908120805483929061330d908490613fb3565b909155505050505050565b6001600160a01b03808316600090815260086020908152604080832093871683529290529081205460ff166133815760405162461bcd60e51b815260206004820152600f60248201526e4e4f545f50554c4c5f534f5552434560881b60448201526064016109f3565b600061338c846130e1565b6040516272333d60e01b81526001600160a01b0386811660048301528581166024830152919250908616906272333d90604401600060405180830381600087803b1580156133d957600080fd5b505af11580156133ed573d6000803e3d6000fd5b5050505060006133fc856130e1565b90506134088282613f40565b9250856001600160a01b0316846001600160a01b0316866001600160a01b03167f1d52d80be7a2d094505cdf4bcddcc841ac4ffde4ab097d1c8c80d431fd7ee0d38660405161345991815260200190565b60405180910390a450509392505050565b6040516001600160a01b03838116602483015260448201839052610a5891859182169063a9059cbb90606401613181565b60006134b06001600160a01b038416836134fe565b905080516000141580156134d55750808060200190518101906134d39190614267565b155b15610a5857604051635274afe760e01b81526001600160a01b03841660048201526024016109f3565b60606117f48383600084600080856001600160a01b03168486604051613524919061434e565b60006040518083038185875af1925050503d8060008114613561576040519150601f19603f3d011682016040523d82523d6000602084013e613566565b606091505b5091509150613576868383613580565b9695505050505050565b60608261359557613590826135dc565b6117f4565b81511580156135ac57506001600160a01b0384163b155b156135d557604051639996b31560e01b81526001600160a01b03851660048201526024016109f3565b50806117f4565b8051156135ec5780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b50805461361190613f79565b6000825580601f10613621575050565b601f0160209004906000526020600020908101906117b591905b8082111561364f576000815560010161363b565b5090565b80356001600160a01b038116811461366a57600080fd5b919050565b60006020828403121561368157600080fd5b6117f482613653565b6000806040838503121561369d57600080fd5b6136a683613653565b91506136b460208401613653565b90509250929050565b6020808252825182820181905260009190848201906040850190845b818110156136fe5783516001600160a01b0316835292840192918401916001016136d9565b50909695505050505050565b60008083601f84011261371c57600080fd5b5081356001600160401b0381111561373357600080fd5b6020830191508360208260051b850101111561374e57600080fd5b9250929050565b6000806000806060858703121561376b57600080fd5b61377485613653565b935061378260208601613653565b925060408501356001600160401b0381111561379d57600080fd5b6137a98782880161370a565b95989497509550505050565b6000602082840312156137c757600080fd5b5035919050565b60008083601f8401126137e057600080fd5b5081356001600160401b038111156137f757600080fd5b60208301915083602082850101111561374e57600080fd5b60008060008060008060006080888a03121561382a57600080fd5b87356001600160401b038082111561384157600080fd5b61384d8b838c016137ce565b909950975087915061386160208b01613653565b965060408a013591508082111561387757600080fd5b6138838b838c016137ce565b909650945060608a013591508082111561389c57600080fd5b506138a98a828b016137ce565b989b979a50959850939692959293505050565b6000806000604084860312156138d157600080fd5b83356001600160401b038111156138e757600080fd5b6138f3868287016137ce565b9094509250613906905060208501613653565b90509250925092565b60005b8381101561392a578181015183820152602001613912565b50506000910152565b6000815180845261394b81602086016020860161390f565b601f01601f19169290920160200192915050565b6040815260006139726040830185613933565b82810360208401526139848185613933565b95945050505050565b600080604083850312156139a057600080fd5b6139a983613653565b946020939093013593505050565b6020815260006117f46020830184613933565b6000806000606084860312156139df57600080fd5b6139e884613653565b92506139f660208501613653565b9150604084013560028110613a0a57600080fd5b809150509250925092565b60008060008060808587031215613a2b57600080fd5b613a3485613653565b9350613a4260208601613653565b925060408501359150613a5760608601613653565b905092959194509250565b634e487b7160e01b600052602160045260246000fd5b6020810160028310613a9a57634e487b7160e01b600052602160045260246000fd5b91905290565b80151581146117b557600080fd5b600060208284031215613ac057600080fd5b81356117f481613aa0565b600080600060408486031215613ae057600080fd5b613ae984613653565b925060208401356001600160401b03811115613b0457600080fd5b613b108682870161370a565b9497909650939450505050565b6020808252825182820181905260009190848201906040850190845b818110156136fe57835183529284019291840191600101613b39565b60008060008060008060808789031215613b6e57600080fd5b613b7787613653565b955060208701356001600160401b0380821115613b9357600080fd5b613b9f8a838b0161370a565b90975095506040890135915080821115613bb857600080fd5b50613bc589828a0161370a565b9094509250613bd8905060608801613653565b90509295509295509295565b634e487b7160e01b600052604160045260246000fd5b60008060408385031215613c0d57600080fd5b613c1683613653565b915060208301356001600160401b0380821115613c3257600080fd5b818501915085601f830112613c4657600080fd5b813581811115613c5857613c58613be4565b604051601f8201601f19908116603f01168101908382118183101715613c8057613c80613be4565b81604052828152886020848701011115613c9957600080fd5b8260208601602083013760006020848301015280955050505050509250929050565b600060208083016020845280855180835260408601915060408160051b87010192506020870160005b82811015613d1257603f19888603018452613d00858351613933565b94509285019290850190600101613ce4565b5092979650505050505050565b60008060008060008060a08789031215613d3857600080fd5b86356001600160401b03811115613d4e57600080fd5b613d5a89828a016137ce565b90975095505060208701359350613d7360408801613653565b9250613d8160608801613653565b9150608087013590509295509295509295565b60008060008060008060008060a0898b031215613db057600080fd5b88356001600160401b0380821115613dc757600080fd5b613dd38c838d016137ce565b909a50985060208b01359750889150613dee60408c01613653565b965060608b0135915080821115613e0457600080fd5b613e108c838d0161370a565b909650945060808b0135915080821115613e2957600080fd5b50613e368b828c0161370a565b999c989b5096995094979396929594505050565b60008060408385031215613e5d57600080fd5b613e6683613653565b91506020830135613e7681613aa0565b809150509250929050565b60208082526013908201527210531317d0d3105253525391d7d4105554d151606a1b604082015260600190565b60208082526019908201527f434c41494d494e475f5041555345445f464f525f544f4b454e00000000000000604082015260600190565b602080825260159082015274141554d2115497d393d517d49151d254d511549151605a1b604082015260600190565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b81810381811115610f6a57610f6a613f2a565b634e487b7160e01b600052603160045260246000fd5b8183823760009101908152919050565b600181811c90821680613f8d57607f821691505b602082108103613fad57634e487b7160e01b600052602260045260246000fd5b50919050565b80820180821115610f6a57610f6a613f2a565b601f821115610a58576000816000526020600020601f850160051c81016020861015613fef5750805b601f850160051c820191505b8181101561400e57828155600101613ffb565b505050505050565b600019600383901b1c191660019190911b1790565b6001600160401b0383111561404257614042613be4565b614056836140508354613f79565b83613fc6565b6000601f84116001811461408457600085156140725750838201355b61407c8682614016565b845550610bde565b600083815260209020601f19861690835b828110156140b55786850135825560209485019460019092019101614095565b50868210156140d25760001960f88860031b161c19848701351681555b505060018560011b0183555050505050565b60208082526018908201527f4e4f545f4f4e474f494e475f44495350454e534154494f4e0000000000000000604082015260600190565b600080835461412981613f79565b60018281168015614141576001811461415657614185565b60ff1984168752821515830287019450614185565b8760005260208060002060005b8581101561417c5781548a820152908401908201614163565b50505082870194505b50929695505050505050565b81810361419c575050565b6141a68254613f79565b6001600160401b038111156141bd576141bd613be4565b6141d1816141cb8454613f79565b84613fc6565b6000601f8211600181146141ff57600083156141ed5750848201545b6141f78482614016565b855550610bde565b600085815260209020601f19841690600086815260209020845b838110156142395782860154825560019586019590910190602001614219565b50858310156142575781850154600019600388901b60f8161c191681555b5050505050600190811b01905550565b60006020828403121561427957600080fd5b81516117f481613aa0565b81516001600160401b0381111561429d5761429d613be4565b6142ab816141cb8454613f79565b602080601f8311600181146142da57600084156142c85750858301515b6142d28582614016565b86555061400e565b600085815260208120601f198616915b82811015614309578886015182559484019460019091019084016142ea565b508582101561425757939096015160001960f8600387901b161c19169092555050600190811b01905550565b60006020828403121561434757600080fd5b5051919050565b6000825161436081846020870161390f565b919091019291505056fea2646970667358221220882c27ef62090255e612daffa5cfd82f9b3605233ffeaccb0eec03330736d58b64736f6c63430008180033

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106103975760003560e01c806379ba5097116101e9578063b7b6287e1161010f578063e30c3978116100ad578063f3695d981161007c578063f3695d98146108c8578063f44978f914610949578063f5f5ba721461095c578063ffb85f5a1461098f57600080fd5b8063e30c3978146108e2578063e50c26b9146108f3578063e9597a8b14610916578063f2fde38b1461093657600080fd5b8063b987b2e8116100e9578063b987b2e814610887578063cc236cc7146108b5578063d1a8ff54146108c8578063dec66036146108cf57600080fd5b8063b7b6287e14610859578063b836441e1461086c578063b8b65cf41461087457600080fd5b80638da5cb5b116101875780639d8ec144116101565780639d8ec144146107e8578063a8bc01a4146107fb578063a9b8c5b41461081b578063b058fa361461084657600080fd5b80638da5cb5b146107a45780638e681ec3146107b55780638e96317f146107c85780639553c578146107db57600080fd5b806386725a75116101c357806386725a7514610720578063874951c3146107335780638aa10435146107725780638bf4d4af1461079157600080fd5b806379ba5097146106f257806379c12917146106fa57806385f9db671461070d57600080fd5b8063330ca298116102ce5780635b2e448f1161026c5780636a274d221161023b5780636a274d221461068c578063715018a6146106b757806374b3ff4f146106bf5780637820c216146106df57600080fd5b80635b2e448f14610602578063614d08f814610615578063647aefbd1461064b578063677625a71461067957600080fd5b806342456d0d116102a857806342456d0d146105805780634b98684f146105935780634eb4164c146105a657806352fc7984146105b957600080fd5b8063330ca2981461051d57806336fa5f081461053e57806338b903331461055157600080fd5b80631a6748f41161033b57806323e86e341161031557806323e86e34146104c1578063247858ba146104d45780632d7ce37f146104e75780632eb37ff4146104fa57600080fd5b80631a6748f414610470578063213e832814610483578063231d89c8146104ae57600080fd5b80630dd7b0cb116103775780630dd7b0cb146104225780630e6adf37146104355780630eceebc51461044a57806311bb93511461045d57600080fd5b80626dff4b1461039c57806272333d146103d4578063099b58fd146103e9575b600080fd5b6103bf6103aa36600461366f565b600d6020526000908152604090205460ff1681565b60405190151581526020015b60405180910390f35b6103e76103e236600461368a565b6109a2565b005b6104146103f736600461368a565b600f60209081526000928352604080842090915290825290205481565b6040519081526020016103cb565b6103e761043036600461368a565b610a5d565b61043d610ac5565b6040516103cb91906136bd565b6103e7610458366004613755565b610b27565b61043d61046b36600461366f565b610be5565b6103e761047e36600461368a565b610c5b565b6104966104913660046137b5565b610e10565b6040516001600160a01b0390911681526020016103cb565b61043d6104bc36600461366f565b610e3a565b6103e76104cf36600461380f565b610eae565b600254610496906001600160a01b031681565b6104146104f536600461368a565b610f35565b6103bf61050836600461366f565b60066020526000908152604090205460ff1681565b61053061052b3660046138bc565b610f70565b6040516103cb92919061395f565b61049661054c36600461398d565b611113565b6105736040518060400160405280600381526020016203130360ec1b81525081565b6040516103cb91906139b7565b6103e761058e36600461368a565b61114b565b6103e76105a13660046139ca565b6111be565b6103e76105b4366004613a15565b61135b565b6105f56105c736600461368a565b6001600160a01b039182166000908152600a6020908152604080832093909416825291909152205460ff1690565b6040516103cb9190613a78565b6103e761061036600461368a565b611375565b6105736040518060400160405280601781526020017647656e6572616c546f6b656e44697370656e736572563160481b81525081565b6103bf61065936600461368a565b600860209081526000928352604080842090915290825290205460ff1681565b6103e7610687366004613aae565b611559565b61041461069a36600461368a565b601060209081526000928352604080842090915290825290205481565b6103e76115ee565b6106d26106cd366004613acb565b611602565b6040516103cb9190613b1d565b6104966106ed36600461398d565b611758565b6103e7611774565b61041461070836600461368a565b6117b8565b6103e761071b366004613b55565b6117fb565b6103e761072e36600461366f565b6118a1565b6103bf610741366004613bfa565b6012602090815260009283526040909220815180830184018051928152908401929093019190912091525460ff1681565b60408051808201909152600381526203130360ec1b6020820152610573565b61057361079f366004613bfa565b6118b2565b6000546001600160a01b0316610496565b6103e76107c336600461380f565b611968565b6103e76107d63660046138bc565b611b32565b6005546103bf9060ff1681565b6105736107f636600461398d565b611d77565b61080e61080936600461366f565b611daf565b6040516103cb9190613cbb565b61041461082936600461368a565b600460209081526000928352604080842090915290825290205481565b6103e7610854366004613d1f565b611e9e565b6104966108673660046137b5565b611f75565b61043d611f85565b610573610882366004613bfa565b611fe5565b6105f561089536600461368a565b600a60209081526000928352604080842090915290825290205460ff1681565b6103e76108c3366004613d94565b61201a565b60016103bf565b6103e76108dd36600461398d565b6121cb565b6001546001600160a01b0316610496565b6103bf61090136600461366f565b600e6020526000908152604090205460ff1681565b61041461092436600461366f565b60036020526000908152604090205481565b6103e761094436600461366f565b6121f3565b6103e7610957366004613e4a565b612264565b60408051808201909152601781527647656e6572616c546f6b656e44697370656e736572563160481b6020820152610573565b6103e761099d36600461368a565b612324565b6002546001600160a01b031633146109fc5760405162461bcd60e51b81526020600482015260186024820152772727aa2fa227aba729aa2922a0a6afa224a9a822a729a2a960411b60448201526064015b60405180910390fd5b6002546001600160a01b0316610a4e5760405162461bcd60e51b81526020600482015260176024820152762727afa227aba729aa2922a0a6afa224a9a822a729a2a960491b60448201526064016109f3565b610a588282612526565b505050565b600554829060ff1615610a825760405162461bcd60e51b81526004016109f390613e81565b6001600160a01b03811660009081526006602052604090205460ff1615610abb5760405162461bcd60e51b81526004016109f390613eae565b610a58838361254c565b6060600c805480602002602001604051908101604052809291908181526020018280548015610b1d57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610aff575b5050505050905090565b600554849060ff1615610b4c5760405162461bcd60e51b81526004016109f390613e81565b6001600160a01b03811660009081526006602052604090205460ff1615610b855760405162461bcd60e51b81526004016109f390613eae565b81610bc85760405162461bcd60e51b81526020600482015260136024820152721393d7d4d3d55490d154d7d41493d592511151606a1b60448201526064016109f3565b610bd483838787612557565b610bde858561254c565b5050505050565b6001600160a01b038116600090815260096020908152604091829020805483518184028101840190945280845260609392830182828015610c4f57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610c31575b50505050509050919050565b610c6361259c565b610c6d82826125c9565b610c895760405162461bcd60e51b81526004016109f390613ee5565b6001600160a01b038281166000818152600a602090815260408083209486168352938152838220805460ff191690559181526009909152908120905b8154811015610dc157826001600160a01b0316828281548110610cea57610cea613f14565b6000918252602090912001546001600160a01b031603610db95781548290610d1490600190613f40565b81548110610d2457610d24613f14565b9060005260206000200160009054906101000a90046001600160a01b0316828281548110610d5457610d54613f14565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555081805480610d9257610d92613f53565b600082815260209020810160001990810180546001600160a01b0319169055019055610dc1565b600101610cc5565b50610dcb83612611565b816001600160a01b0316836001600160a01b03167f5144cb6540b0046ea54d1878787028f5635d75813e2aaf25ea0904fc69f47e3360405160405180910390a3505050565b600b8181548110610e2057600080fd5b6000918252602090912001546001600160a01b0316905081565b6001600160a01b038116600090815260076020908152604091829020805483518184028101840190945280845260609392830182828015610c4f576020028201919060005260206000209081546001600160a01b03168152600190910190602001808311610c315750505050509050919050565b610eb661259c565b610f2c87878787878080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050604080516020601f8b01819004810282018101909252898152925089915088908190840183828082843760009201919091525061279192505050565b50505050505050565b6000610f418383612881565b50506001600160a01b038083166000908152600460209081526040808320938516835292905220545b92915050565b6001600160a01b0381166000908152601360205260409081902090516060918291610f9e9087908790613f69565b908152602001604051809103902060146000856001600160a01b03166001600160a01b031681526020019081526020016000208686604051610fe1929190613f69565b9081526020016040518091039020818054610ffb90613f79565b80601f016020809104026020016040519081016040528092919081815260200182805461102790613f79565b80156110745780601f1061104957610100808354040283529160200191611074565b820191906000526020600020905b81548152906001019060200180831161105757829003601f168201915b5050505050915080805461108790613f79565b80601f01602080910402602001604051908101604052809291908181526020018280546110b390613f79565b80156111005780601f106110d557610100808354040283529160200191611100565b820191906000526020600020905b8154815290600101906020018083116110e357829003601f168201915b5050505050905091509150935093915050565b6007602052816000526040600020818154811061112f57600080fd5b6000918252602090912001546001600160a01b03169150829050565b600554829060ff16156111705760405162461bcd60e51b81526004016109f390613e81565b6001600160a01b03811660009081526006602052604090205460ff16156111a95760405162461bcd60e51b81526004016109f390613eae565b6111b38383612881565b50610a58838361254c565b6111c661259c565b6111d083836125c9565b1561121d5760405162461bcd60e51b815260206004820152601960248201527f5055534845525f414c52454144595f524547495354455245440000000000000060448201526064016109f3565b600081600181111561123157611231613a62565b0361127e5760405162461bcd60e51b815260206004820152601a60248201527f43414e4e4f545f52454749535445525f4e4f4e455f4c4556454c00000000000060448201526064016109f3565b6001600160a01b038084166000908152600a60209081526040808320938616835292905220805482919060ff1916600183818111156112bf576112bf613a62565b02179055506001600160a01b0383811660009081526009602090815260408220805460018101825590835291200180546001600160a01b03191691841691909117905561130b8361293f565b816001600160a01b0316836001600160a01b03167f196fa3ab7946db556eb94c3423dc51d780f3a397bcaecd284b44456dc88324d98360405161134e9190613a78565b60405180910390a3505050565b61136361259c565b61136f84848484612aa7565b50505050565b61137d61259c565b6001600160a01b0382166000908152600760205260408120815b815481101561149757836001600160a01b03168282815481106113bc576113bc613f14565b6000918252602090912001546001600160a01b03160361148f57815482906113e690600190613f40565b815481106113f6576113f6613f14565b9060005260206000200160009054906101000a90046001600160a01b031682828154811061142657611426613f14565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b031602179055508180548061146457611464613f53565b600082815260209020810160001990810180546001600160a01b031916905501905560019250611497565b600101611397565b50816114dd5760405162461bcd60e51b81526020600482015260156024820152741415531317d4d3d55490d157d393d517d193d55391605a1b60448201526064016109f3565b6001600160a01b038085166000908152600860209081526040808320938716835292905220805460ff1916905561151384612611565b826001600160a01b0316846001600160a01b03167f493b1bf532df7004894c240578a101f10cbb9216cc9b787dbe3cd9a81b52c4e460405160405180910390a350505050565b61156161259c565b60055481151560ff9091161515036115a95760405162461bcd60e51b815260206004820152600b60248201526a1053149150511657d4d15560aa1b60448201526064016109f3565b6005805460ff191682151590811790915560405160ff9091161515907fa042e27caefe535b8657a25f0d53a680853b51681d7d510291f72ab708b72bf090600090a250565b6115f661259c565b6116006000612c39565b565b6002546060906001600160a01b0316331461165a5760405162461bcd60e51b81526020600482015260186024820152772727aa2fa227aba729aa2922a0a6afa224a9a822a729a2a960411b60448201526064016109f3565b6002546001600160a01b03166116ac5760405162461bcd60e51b81526020600482015260176024820152762727afa227aba729aa2922a0a6afa224a9a822a729a2a960491b60448201526064016109f3565b816001600160401b038111156116c4576116c4613be4565b6040519080825280602002602001820160405280156116ed578160200160208202803683370190505b50905060005b828110156117505761172b8585858481811061171157611711613f14565b9050602002016020810190611726919061366f565b612526565b82828151811061173d5761173d613f14565b60209081029190910101526001016116f3565b509392505050565b6009602052816000526040600020818154811061112f57600080fd5b60015433906001600160a01b031681146117ac5760405163118cdaa760e01b81526001600160a01b03821660048201526024016109f3565b6117b581612c39565b50565b60006117c48383612c52565b6001600160a01b038085166000908152600460209081526040808320938716835292905220546117f49190613fb3565b9392505050565b61180361259c565b8382146118445760405162461bcd60e51b815260206004820152600f60248201526e0988a9c8ea890be9a92a69a82a8869608b1b60448201526064016109f3565b60005b84811015610f2c576118998787878481811061186557611865613f14565b905060200201602081019061187a919061366f565b86868581811061188c5761188c613f14565b9050602002013585612aa7565b600101611847565b6118a961259c565b6117b581612d74565b60146020908152600092835260409092208151808301840180519281529084019290930191909120915280546118e790613f79565b80601f016020809104026020016040519081016040528092919081815260200182805461191390613f79565b80156119605780601f1061193557610100808354040283529160200191611960565b820191906000526020600020905b81548152906001019060200180831161194357829003601f168201915b505050505081565b61197061259c565b6001600160a01b0385166000908152601260205260409081902090516119999089908990613f69565b9081526040519081900360200190205460ff16156119eb5760405162461bcd60e51b815260206004820152600f60248201526e414c52454144595f4f4e474f494e4760881b60448201526064016109f3565b6001600160a01b03851660009081526012602052604090819020905160019190611a18908a908a90613f69565b9081526040805160209281900383019020805460ff1916931515939093179092556001600160a01b0387166000908152601182529182208054600181018255908352912001611a6887898361402b565b50611adf87878787878080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050604080516020601f8b01819004810282018101909252898152925089915088908190840183828082843760009201919091525061279192505050565b846001600160a01b03168787604051611af9929190613f69565b604051908190038120907f2a9b4fc367f7e38f84011e08df7345c49bc5ab25bf5a6fa7a1c7c02f592ba8e490600090a350505050505050565b611b3a61259c565b6001600160a01b038116600090815260126020526040908190209051611b639085908590613f69565b9081526040519081900360200190205460ff16611b925760405162461bcd60e51b81526004016109f3906140e4565b6001600160a01b0381166000908152601260205260408082209051611bba9086908690613f69565b9081526040805160209281900383019020805460ff1916931515939093179092556001600160a01b03831660009081526011909152908120905b8154811015611cfb578484604051602001611c10929190613f69565b60405160208183030381529060405280519060200120828281548110611c3857611c38613f14565b90600052602060002001604051602001611c52919061411b565b6040516020818303038152906040528051906020012003611cf35781548290611c7d90600190613f40565b81548110611c8d57611c8d613f14565b90600052602060002001828281548110611ca957611ca9613f14565b906000526020600020019081611cbf9190614191565b5081805480611cd057611cd0613f53565b600190038181906000526020600020016000611cec9190613605565b9055611cfb565b600101611bf4565b50611d278484846040518060200160405280600081525060405180602001604052806000815250612791565b816001600160a01b03168484604051611d41929190613f69565b604051908190038120907fab45c658146b5490390503d9570ee0393d223a399a8b147e4a31f164e6b3dab890600090a350505050565b60116020528160005260406000208181548110611d9357600080fd5b906000526020600020016000915091505080546118e790613f79565b6001600160a01b0381166000908152601160209081526040808320805482518185028101850190935280835260609492939192909184015b82821015611e93578382906000526020600020018054611e0690613f79565b80601f0160208091040260200160405190810160405280929190818152602001828054611e3290613f79565b8015611e7f5780601f10611e5457610100808354040283529160200191611e7f565b820191906000526020600020905b815481529060010190602001808311611e6257829003601f168201915b505050505081526020019060010190611de7565b505050509050919050565b33611ea984826125c9565b611ec55760405162461bcd60e51b81526004016109f390613ee5565b6001600160a01b038416600090815260126020526040908190209051611eee9089908990613f69565b9081526040519081900360200190205460ff16611f1d5760405162461bcd60e51b81526004016109f3906140e4565b60008211611f5b5760405162461bcd60e51b815260206004820152600b60248201526a414d4f554e545f5a45524f60a81b60448201526064016109f3565b611f66843384612dc6565b610f2c87878787878787612e45565b600c8181548110610e2057600080fd5b6060600b805480602002602001604051908101604052809291908181526020018280548015610b1d576020028201919060005260206000209081546001600160a01b03168152600190910190602001808311610aff575050505050905090565b60136020908152600092835260409092208151808301840180519281529084019290930191909120915280546118e790613f79565b82811461205b5760405162461bcd60e51b815260206004820152600f60248201526e0988a9c8ea890be9a92a69a82a8869608b1b60448201526064016109f3565b3361206686826125c9565b6120825760405162461bcd60e51b81526004016109f390613ee5565b6001600160a01b0386166000908152601260205260409081902090516120ab908b908b90613f69565b9081526040519081900360200190205460ff166120da5760405162461bcd60e51b81526004016109f3906140e4565b6000805b85811015612114578484828181106120f8576120f8613f14565b905060200201358261210a9190613fb3565b91506001016120de565b50600081116121535760405162461bcd60e51b815260206004820152600b60248201526a414d4f554e545f5a45524f60a81b60448201526064016109f3565b61215e878383612dc6565b60005b858110156121be576121b68b8b8b8b8b8b8781811061218257612182613f14565b9050602002016020810190612197919061366f565b8a8a888181106121a9576121a9613f14565b9050602002013589612e45565b600101612161565b5050505050505050505050565b6121d361259c565b6121ef826121e96000546001600160a01b031690565b83612ece565b5050565b6121fb61259c565b600180546001600160a01b0383166001600160a01b0319909116811790915561222c6000546001600160a01b031690565b6001600160a01b03167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a350565b61226c61259c565b6001600160a01b03821660009081526006602052604090205481151560ff9091161515036122ca5760405162461bcd60e51b815260206004820152600b60248201526a1053149150511657d4d15560aa1b60448201526064016109f3565b6001600160a01b038216600081815260066020526040808220805460ff1916851515908117909155905160ff909116151592917fe766dd0694ff9295340d29c34dbc30b4f70c083307c3c81e5285dba3c073274991a35050565b61232c61259c565b806001600160a01b031663f3695d986040518163ffffffff1660e01b8152600401602060405180830381865afa15801561236a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061238e9190614267565b6123cf5760405162461bcd60e51b81526020600482015260126024820152714e4f545f50554c4c5f534f555243455f563160701b60448201526064016109f3565b6001600160a01b0382166000908152600760205260408120905b815481101561247857826001600160a01b031682828154811061240e5761240e613f14565b6000918252602090912001546001600160a01b0316036124705760405162461bcd60e51b815260206004820152601960248201527f50554c4c5f534f555243455f414c52454144595f41444445440000000000000060448201526064016109f3565b6001016123e9565b506001600160a01b0383811660008181526007602090815260408083208054600180820183559185528385200180546001600160a01b03191696891696871790559383526008825280832094835293905291909120805460ff191690911790556124e18361293f565b816001600160a01b0316836001600160a01b03167f46f9eb8ce96dd38714a7b9ac42bdc69a3313c60e548cf5ef032570b793045fdd60405160405180910390a3505050565b60006125328383612881565b506002546117f490849084906001600160a01b0316612f47565b610a58828283612f47565b60005b83811015610bde5761259385858381811061257757612577613f14565b905060200201602081019061258c919061366f565b8484613063565b5060010161255a565b6000546001600160a01b031633146116005760405163118cdaa760e01b81523360048201526024016109f3565b6001600160a01b038083166000908152600a6020908152604080832093851683529290529081205460ff168181600181111561260757612607613a62565b1415949350505050565b6001600160a01b03811660009081526007602090815260408083205460099092529091205481158015612642575080155b15610a585760005b600b5481101561274557836001600160a01b0316600b828154811061267157612671613f14565b6000918252602090912001546001600160a01b03160361273d57600b805461269b90600190613f40565b815481106126ab576126ab613f14565b600091825260209091200154600b80546001600160a01b0390921691839081106126d7576126d7613f14565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550600b80548061271657612716613f53565b600082815260209020810160001990810180546001600160a01b0319169055019055612745565b60010161264a565b506001600160a01b0383166000818152600e6020526040808220805460ff19169055517f5458626fa48fdcbb7a33a1f144e182222b16ecfdd862b844752fc0061c55b45a9190a2505050565b6001600160a01b0383166000908152601360205260409081902090518391906127bd9088908890613f69565b908152602001604051809103902090816127d79190614284565b506001600160a01b0383166000908152601460205260409081902090518291906128049088908890613f69565b9081526020016040518091039020908161281e9190614284565b50826001600160a01b03168585604051612839929190613f69565b60405180910390207f6580caef4438dbfe0b139727fbf172f51a284bf6e86fca731f02eea21ca58097848460405161287292919061395f565b60405180910390a35050505050565b6001600160a01b0382166000908152600760209081526040808320805482518185028101850190935280835284938301828280156128e857602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116128ca575b505050505090506000915060005b81518110156129375761292382828151811061291457612914613f14565b60200260200101518686613063565b61292d9084613fb3565b92506001016128f6565b505092915050565b6001600160a01b0381166000908152600e602052604090205460ff168015612965575050565b6001600160a01b0382166000908152600d602052604090205460ff16612a1257600c805460018082019092557fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c70180546001600160a01b0319166001600160a01b0385169081179091556000818152600d6020526040808220805460ff1916909417909355915190917ffef74848d3638a6ed3547447282ce33584cf17be953e3f9f7c6824e2284969e991a25b806121ef57600b805460018082019092557f0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01db90180546001600160a01b0319166001600160a01b0385169081179091556000818152600e6020526040808220805460ff1916909417909355915190917f74e2d6b252841daa9d389cafd5dca3a3927ef59ebfa28699c1748c1a1603250e91a25050565b612ab18484612881565b506001600160a01b03808516600090815260046020908152604080832093871683529290529081205490838103612b1157506001600160a01b03808616600090815260046020908152604080832093881683529290529081205580612b8d565b83821015612b5a5760405162461bcd60e51b8152602060048201526016602482015275414d4f554e545f455843454544535f50454e44494e4760501b60448201526064016109f3565b612b648483613f40565b6001600160a01b038088166000908152600460209081526040808320938a168352929052205550825b6001600160a01b038616600090815260036020526040902054808211612bbc57612bb78282613f40565b612bbf565b60005b6001600160a01b038816600090815260036020526040902055612be387858461307d565b856001600160a01b0316876001600160a01b03167f13268b3f271140c93c5d8b166950c19ad5320c82e78b1aa918bfb842ff13454584604051612c2891815260200190565b60405180910390a350505050505050565b600180546001600160a01b03191690556117b581613091565b6001600160a01b038216600090815260076020908152604080832080548251818502810185019093528083528493830182828015612cb957602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311612c9b575b505050505090506000915060005b815181101561293757818181518110612ce257612ce2613f14565b60209081029190910101516040516379c1291760e01b81526001600160a01b0387811660048301528681166024830152909116906379c1291790604401602060405180830381865afa158015612d3c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d609190614335565b612d6a9084613fb3565b9250600101612cc7565b600280546001600160a01b038381166001600160a01b0319831681179093556040519116919082907ff10a1acd40d60bee3d5b658d910c1f9ab77a0fe972c5de7774cd8404d65bc66990600090a35050565b6000612dd1846130e1565b9050612de86001600160a01b03851684308561314c565b6000612df3856130e1565b905082612e008383613f40565b14610bde5760405162461bcd60e51b815260206004820152601560248201527411125117d393d517d49150d152559157d1561050d5605a1b60448201526064016109f3565b612e508484846131b3565b826001600160a01b0316846001600160a01b03168888604051612e74929190613f69565b604080519182900382206001600160a01b0386168352602083018a9052908201869052907fae2504b8c277e24642789ad5fb484179bdb5a5f4dc86b63f89e21788677f3c109060600160405180910390a450505050505050565b6000808211612ee557612ee0846130e1565b612ee7565b815b9050612ef484848361307d565b826001600160a01b0316846001600160a01b03167fd092d7fceb5ea5a962639fcc27a7bb315e7637e699e3b108cd570c38c758430083604051612f3991815260200190565b60405180910390a350505050565b60006001600160a01b038216612f9f5760405162461bcd60e51b815260206004820152601760248201527f43414e4e4f545f44495350454e53455f544f5f5a45524f00000000000000000060448201526064016109f3565b506001600160a01b038084166000818152600460209081526040808320948716835293815283822080549083905592825260039052918220805491928392612fe8908490613f40565b90915550612ffa90508484848461327b565b61300584838361307d565b816001600160a01b0316836001600160a01b0316856001600160a01b03167f365c82a2bdc395fa2c986a8aa602cb7c0d691b728b80765d104b8a879b3b484f8460405161305491815260200190565b60405180910390a49392505050565b6000613070848484613318565b90506117f48383836131b3565b610a586001600160a01b038416838361346a565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6040516370a0823160e01b81523060048201526000906001600160a01b038316906370a0823190602401602060405180830381865afa158015613128573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f6a9190614335565b6040516001600160a01b03848116602483015283811660448301526064820183905261136f9186918216906323b872dd906084015b604051602081830303815290604052915060e01b6020820180516001600160e01b03838183161783525050505061349b565b6001600160a01b0380841660009081526004602090815260408083209386168352929052908120546131e6908390613fb3565b6001600160a01b0385166000908152600360205260408120549192509061320e908490613fb3565b6001600160a01b038681166000818152600460209081526040808320948a1680845294825280832088905583835260038252918290208590559051878152939450919290917fd6f1b1200b83332fb4d7e0f91e1f1672c77bb36ac9954f1b998d48f1a155434e9101612872565b816001600160a01b0316836001600160a01b0316036132d6576001600160a01b038085166000908152600f60209081526040808320938716835292905290812080548392906132cb908490613fb3565b9091555061136f9050565b6001600160a01b0380851660009081526010602090815260408083209387168352929052908120805483929061330d908490613fb3565b909155505050505050565b6001600160a01b03808316600090815260086020908152604080832093871683529290529081205460ff166133815760405162461bcd60e51b815260206004820152600f60248201526e4e4f545f50554c4c5f534f5552434560881b60448201526064016109f3565b600061338c846130e1565b6040516272333d60e01b81526001600160a01b0386811660048301528581166024830152919250908616906272333d90604401600060405180830381600087803b1580156133d957600080fd5b505af11580156133ed573d6000803e3d6000fd5b5050505060006133fc856130e1565b90506134088282613f40565b9250856001600160a01b0316846001600160a01b0316866001600160a01b03167f1d52d80be7a2d094505cdf4bcddcc841ac4ffde4ab097d1c8c80d431fd7ee0d38660405161345991815260200190565b60405180910390a450509392505050565b6040516001600160a01b03838116602483015260448201839052610a5891859182169063a9059cbb90606401613181565b60006134b06001600160a01b038416836134fe565b905080516000141580156134d55750808060200190518101906134d39190614267565b155b15610a5857604051635274afe760e01b81526001600160a01b03841660048201526024016109f3565b60606117f48383600084600080856001600160a01b03168486604051613524919061434e565b60006040518083038185875af1925050503d8060008114613561576040519150601f19603f3d011682016040523d82523d6000602084013e613566565b606091505b5091509150613576868383613580565b9695505050505050565b60608261359557613590826135dc565b6117f4565b81511580156135ac57506001600160a01b0384163b155b156135d557604051639996b31560e01b81526001600160a01b03851660048201526024016109f3565b50806117f4565b8051156135ec5780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b50805461361190613f79565b6000825580601f10613621575050565b601f0160209004906000526020600020908101906117b591905b8082111561364f576000815560010161363b565b5090565b80356001600160a01b038116811461366a57600080fd5b919050565b60006020828403121561368157600080fd5b6117f482613653565b6000806040838503121561369d57600080fd5b6136a683613653565b91506136b460208401613653565b90509250929050565b6020808252825182820181905260009190848201906040850190845b818110156136fe5783516001600160a01b0316835292840192918401916001016136d9565b50909695505050505050565b60008083601f84011261371c57600080fd5b5081356001600160401b0381111561373357600080fd5b6020830191508360208260051b850101111561374e57600080fd5b9250929050565b6000806000806060858703121561376b57600080fd5b61377485613653565b935061378260208601613653565b925060408501356001600160401b0381111561379d57600080fd5b6137a98782880161370a565b95989497509550505050565b6000602082840312156137c757600080fd5b5035919050565b60008083601f8401126137e057600080fd5b5081356001600160401b038111156137f757600080fd5b60208301915083602082850101111561374e57600080fd5b60008060008060008060006080888a03121561382a57600080fd5b87356001600160401b038082111561384157600080fd5b61384d8b838c016137ce565b909950975087915061386160208b01613653565b965060408a013591508082111561387757600080fd5b6138838b838c016137ce565b909650945060608a013591508082111561389c57600080fd5b506138a98a828b016137ce565b989b979a50959850939692959293505050565b6000806000604084860312156138d157600080fd5b83356001600160401b038111156138e757600080fd5b6138f3868287016137ce565b9094509250613906905060208501613653565b90509250925092565b60005b8381101561392a578181015183820152602001613912565b50506000910152565b6000815180845261394b81602086016020860161390f565b601f01601f19169290920160200192915050565b6040815260006139726040830185613933565b82810360208401526139848185613933565b95945050505050565b600080604083850312156139a057600080fd5b6139a983613653565b946020939093013593505050565b6020815260006117f46020830184613933565b6000806000606084860312156139df57600080fd5b6139e884613653565b92506139f660208501613653565b9150604084013560028110613a0a57600080fd5b809150509250925092565b60008060008060808587031215613a2b57600080fd5b613a3485613653565b9350613a4260208601613653565b925060408501359150613a5760608601613653565b905092959194509250565b634e487b7160e01b600052602160045260246000fd5b6020810160028310613a9a57634e487b7160e01b600052602160045260246000fd5b91905290565b80151581146117b557600080fd5b600060208284031215613ac057600080fd5b81356117f481613aa0565b600080600060408486031215613ae057600080fd5b613ae984613653565b925060208401356001600160401b03811115613b0457600080fd5b613b108682870161370a565b9497909650939450505050565b6020808252825182820181905260009190848201906040850190845b818110156136fe57835183529284019291840191600101613b39565b60008060008060008060808789031215613b6e57600080fd5b613b7787613653565b955060208701356001600160401b0380821115613b9357600080fd5b613b9f8a838b0161370a565b90975095506040890135915080821115613bb857600080fd5b50613bc589828a0161370a565b9094509250613bd8905060608801613653565b90509295509295509295565b634e487b7160e01b600052604160045260246000fd5b60008060408385031215613c0d57600080fd5b613c1683613653565b915060208301356001600160401b0380821115613c3257600080fd5b818501915085601f830112613c4657600080fd5b813581811115613c5857613c58613be4565b604051601f8201601f19908116603f01168101908382118183101715613c8057613c80613be4565b81604052828152886020848701011115613c9957600080fd5b8260208601602083013760006020848301015280955050505050509250929050565b600060208083016020845280855180835260408601915060408160051b87010192506020870160005b82811015613d1257603f19888603018452613d00858351613933565b94509285019290850190600101613ce4565b5092979650505050505050565b60008060008060008060a08789031215613d3857600080fd5b86356001600160401b03811115613d4e57600080fd5b613d5a89828a016137ce565b90975095505060208701359350613d7360408801613653565b9250613d8160608801613653565b9150608087013590509295509295509295565b60008060008060008060008060a0898b031215613db057600080fd5b88356001600160401b0380821115613dc757600080fd5b613dd38c838d016137ce565b909a50985060208b01359750889150613dee60408c01613653565b965060608b0135915080821115613e0457600080fd5b613e108c838d0161370a565b909650945060808b0135915080821115613e2957600080fd5b50613e368b828c0161370a565b999c989b5096995094979396929594505050565b60008060408385031215613e5d57600080fd5b613e6683613653565b91506020830135613e7681613aa0565b809150509250929050565b60208082526013908201527210531317d0d3105253525391d7d4105554d151606a1b604082015260600190565b60208082526019908201527f434c41494d494e475f5041555345445f464f525f544f4b454e00000000000000604082015260600190565b602080825260159082015274141554d2115497d393d517d49151d254d511549151605a1b604082015260600190565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b81810381811115610f6a57610f6a613f2a565b634e487b7160e01b600052603160045260246000fd5b8183823760009101908152919050565b600181811c90821680613f8d57607f821691505b602082108103613fad57634e487b7160e01b600052602260045260246000fd5b50919050565b80820180821115610f6a57610f6a613f2a565b601f821115610a58576000816000526020600020601f850160051c81016020861015613fef5750805b601f850160051c820191505b8181101561400e57828155600101613ffb565b505050505050565b600019600383901b1c191660019190911b1790565b6001600160401b0383111561404257614042613be4565b614056836140508354613f79565b83613fc6565b6000601f84116001811461408457600085156140725750838201355b61407c8682614016565b845550610bde565b600083815260209020601f19861690835b828110156140b55786850135825560209485019460019092019101614095565b50868210156140d25760001960f88860031b161c19848701351681555b505060018560011b0183555050505050565b60208082526018908201527f4e4f545f4f4e474f494e475f44495350454e534154494f4e0000000000000000604082015260600190565b600080835461412981613f79565b60018281168015614141576001811461415657614185565b60ff1984168752821515830287019450614185565b8760005260208060002060005b8581101561417c5781548a820152908401908201614163565b50505082870194505b50929695505050505050565b81810361419c575050565b6141a68254613f79565b6001600160401b038111156141bd576141bd613be4565b6141d1816141cb8454613f79565b84613fc6565b6000601f8211600181146141ff57600083156141ed5750848201545b6141f78482614016565b855550610bde565b600085815260209020601f19841690600086815260209020845b838110156142395782860154825560019586019590910190602001614219565b50858310156142575781850154600019600388901b60f8161c191681555b5050505050600190811b01905550565b60006020828403121561427957600080fd5b81516117f481613aa0565b81516001600160401b0381111561429d5761429d613be4565b6142ab816141cb8454613f79565b602080601f8311600181146142da57600084156142c85750858301515b6142d28582614016565b86555061400e565b600085815260208120601f198616915b82811015614309578886015182559484019460019091019084016142ea565b508582101561425757939096015160001960f8600387901b161c19169092555050600190811b01905550565b60006020828403121561434757600080fd5b5051919050565b6000825161436081846020870161390f565b919091019291505056fea2646970667358221220882c27ef62090255e612daffa5cfd82f9b3605233ffeaccb0eec03330736d58b64736f6c63430008180033

Block Transaction Gas Used Reward
view all blocks produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
[ Download: CSV Export  ]

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.