Contract 0x469C6F4c1AdA45EB2E251685aC2bf05aEd591E70 13

 

Contract Overview

PoolTogether: TWAB Delegator
Balance:
0 ETH

EtherValue:
$0.00

Token:
Txn Hash Method
Block
From
To
Value
0x2883ed17d7cc7ad67955359cf80ff31f1706e08e426106d65f1cfbafad2620bdMulticall1167681822024-02-28 15:45:414 days 16 hrs ago0x256f8bd0ff25577143332ae43699e4a0157eaa26 IN  PoolTogether: TWAB Delegator0 ETH0.0002125102360.125969468
0xb10879e88e05fdb6ff496e4e7743c48227994ec8c7e7ea592d58128d2f39f07aPermit And Multi...1165830532024-02-24 8:54:438 days 23 hrs ago0x256f8bd0ff25577143332ae43699e4a0157eaa26 IN  PoolTogether: TWAB Delegator0 ETH0.0001173151340.003039386
0x89fb4684d3ca195a8402536d6933eddbb23a5975faed1776ff3806954df16fc7Multicall1163913872024-02-19 22:25:5113 days 10 hrs ago0xf89bb80788a728688015765c8f4b75f96a87a5b3 IN  PoolTogether: TWAB Delegator0 ETH0.0001289418690.019702357
0xe8476bea32d791aa099f4c4232bd28c60617d134861e62cbb73fc2977c485c03Permit And Multi...1152155572024-01-23 17:11:3140 days 15 hrs ago0x52d34151cc547e9577a86cee54c95e73f5e58193 IN  PoolTogether: TWAB Delegator0 ETH0.0000933039570.005292539
0x79286e0d17a5509aba450b5927c44b6f7f9d1da09bd285d7f1afe32372a440a5Multicall1144389212024-01-05 17:43:3958 days 14 hrs ago0xb525f6b3b5efcc0c54582de2f08d890f7793e3e4 IN  PoolTogether: TWAB Delegator0 ETH0.0000739197270.007999594
0xa421769c3ea8a16931f2887cdbc7f9a4ea9f38e4e9f7612d9f29834902573b34Multicall1144388162024-01-05 17:40:0958 days 14 hrs ago0xb525f6b3b5efcc0c54582de2f08d890f7793e3e4 IN  PoolTogether: TWAB Delegator0 ETH0.0000814512820.008818881
0x81bd55d9353cd49af9030e5a40a8e1b09fb1444f268852eeb5e64cfc56e33626Multicall1139648242023-12-25 18:20:2569 days 14 hrs ago0x33b81b6130e0684c140a110d921fa307b48991d6 IN  PoolTogether: TWAB Delegator0 ETH0.000055847890.008884696
0xc863b5941366383ddeddfac77b442d53cbdf750703cae73dab97ecf0fb130b9fPermit And Multi...1136636112023-12-18 18:59:5976 days 13 hrs ago0x55e2e3016631055c4f15cae2163308d6681a57e6 IN  PoolTogether: TWAB Delegator0 ETH0.0008650918420.108501938
0xca7288bbfed0e2ee7b5b1625f305a310dbc90e645736def35ec44d33af2fa68fPermit And Multi...1126270162023-11-24 19:06:49100 days 13 hrs ago0x5f26cc1c4fd6f9b0db0722f4e6737bc547352c46 IN  PoolTogether: TWAB Delegator0 ETH0.0001007446050.010622908
0x852a6446e0b96e94abaa152b361386af56c60b1f60179e7b83fad0f1db365921Multicall1124226862023-11-20 1:35:49105 days 7 hrs ago0x6fac39cbfb03c2bc75e20c7e4499396f453e449e IN  PoolTogether: TWAB Delegator0 ETH0.0000497316350.0110798
0xd5bbc2a8c5851d4ada3debc7d19d4de99b830bfdee330bb0deefcb9566d0b9b5Multicall1123246172023-11-17 19:06:51107 days 13 hrs ago0x373e676c94f4f14889b6355155098309c63ba1a8 IN  PoolTogether: TWAB Delegator0 ETH0.0000874355820.0102734
0x63b3501880541c3890ac61225234693464ecd4698357ec352c4cb3fcdac70cfcMulticall1121378542023-11-13 11:21:25111 days 21 hrs ago 0x6f69286d7c51de0f0cf5f3c03c8ca3eda8a322f9 IN  PoolTogether: TWAB Delegator0 ETH0.0000831400340.00862306
0xdbb3cc7a9b5cd5bc8fb820db6ee8cf1a578e8b80b5a4ed4635cd9912e48baab6Multicall1119582572023-11-09 7:34:51116 days 1 hr ago0x25ce162063cb8f7ab068d5dae666db6130b710ee IN  PoolTogether: TWAB Delegator0 ETH0.0000686840830.0111652
0xfd3e82a5141c14a4def3db025420d153b72ab94991ee963c963c981fb9a2d23eMulticall1116237872023-11-01 13:45:51123 days 18 hrs ago0x4d1243b6f45d209bd59be55be1609219fd93dc3c IN  PoolTogether: TWAB Delegator0 ETH0.0000858915240.013405416
0xc89ca9854b4e02da19f9731f6dffe11989d42e4c42991e18f71dcf11cff27211Multicall1115822532023-10-31 14:41:23124 days 17 hrs ago0x0e9b063789909565ceda1fba162474405a151e66 IN  PoolTogether: TWAB Delegator0 ETH0.0000566672320.012731101
0xf8a151a0c4425d55160247472f11a69d5d22f1a50a334ebd369238dff2b4f7e0Permit And Multi...1113143112023-10-25 9:49:59130 days 22 hrs ago 0x24c94d6827c43b31102aeae49643a0ff422a9d7d IN  PoolTogether: TWAB Delegator0 ETH0.0000445778490.020460918
0x5fd0af5825b4d8220456deb7ced08af2beedff09e5b5061ea966ef8f55ab46fePermit And Multi...1110174402023-10-18 12:54:17137 days 19 hrs ago0xae983237b3e202d5cbeff1edf071fd45158472a3 IN  PoolTogether: TWAB Delegator0 ETH0.0000288310360.012963713
0xb67f92ab884a943be8a55d8f4c85d1f554fb8bc445c85e423b1ee350a071e47aPermit And Multi...1110174262023-10-18 12:53:49137 days 19 hrs ago0xae983237b3e202d5cbeff1edf071fd45158472a3 IN  PoolTogether: TWAB Delegator0 ETH0.0000304977140.012064807
0x43e68ea764c6b1aad47f89483c11d6add73d6d700b7b0d6dec9a22d125685485Multicall1105591942023-10-07 22:19:25148 days 10 hrs ago0xfa97761428da9686b65bfa73dd9f0b5e028dfcc7 IN  PoolTogether: TWAB Delegator0 ETH0.0000173955480.017407117
0xe52ba004d454a6b127fa0bfe7e2dcb87900ccd5286da45509acd1714684f1d7bPermit And Multi...1105561152023-10-07 20:36:47148 days 12 hrs ago0xfa97761428da9686b65bfa73dd9f0b5e028dfcc7 IN  PoolTogether: TWAB Delegator0 ETH0.0000199210860.016891078
0x6f29c3db7ab3199e14bd760982f2cc4986f941684e97be2e8588afbf77bfb550Permit And Multi...1105560452023-10-07 20:34:27148 days 12 hrs ago0xfa97761428da9686b65bfa73dd9f0b5e028dfcc7 IN  PoolTogether: TWAB Delegator0 ETH0.0000197719760.019076104
0x7a98965bfa1022083c22628106e18c62da33ba0e26a798f4f69879d6ffc5c5f3Permit And Multi...1105516312023-10-07 18:07:19148 days 14 hrs ago0x2464671a8b7d1bcd07a5b635528e0b88fda261e4 IN  PoolTogether: TWAB Delegator0 ETH0.0000397429080.1205493
0x2d35bbce5d210f54d3e0882e9db39f72c4ef34d73425ec28463230033c550aacPermit And Multi...1105333642023-10-07 7:58:25149 days 41 mins ago0xfa97761428da9686b65bfa73dd9f0b5e028dfcc7 IN  PoolTogether: TWAB Delegator0 ETH0.0000322528970.017807012
0x60cf30d1b0108a67a15165dbfa122740b0c69f715613a0fe5fd0b54eada3ecbcPermit And Multi...1101249402023-09-27 21:04:17158 days 11 hrs ago0x37bfb343c57060da89a3c2656c91c85854473762 IN  PoolTogether: TWAB Delegator0 ETH0.0000746768520.04973353
0x40a0f0c09aaea4ebe2c3ea19c38479b53662b3581ed7b4ec163710ad5320aa5ePermit And Multi...1101248102023-09-27 20:59:57158 days 11 hrs ago0x37bfb343c57060da89a3c2656c91c85854473762 IN  PoolTogether: TWAB Delegator0 ETH0.0000515912230.055070598
[ Download CSV Export 

OVERVIEW

PoolTogether is a protocol for no-loss prize games.

Latest 25 internal transaction
Parent Txn Hash Block From To Value
0xb10879e88e05fdb6ff496e4e7743c48227994ec8c7e7ea592d58128d2f39f07a1165830532024-02-24 8:54:438 days 23 hrs ago PoolTogether: TWAB Delegator  Contract Creation0 ETH
0xe8476bea32d791aa099f4c4232bd28c60617d134861e62cbb73fc2977c485c031152155572024-01-23 17:11:3140 days 15 hrs ago PoolTogether: TWAB Delegator  Contract Creation0 ETH
0x2d35bbce5d210f54d3e0882e9db39f72c4ef34d73425ec28463230033c550aac1105333642023-10-07 7:58:25149 days 41 mins ago PoolTogether: TWAB Delegator  Contract Creation0 ETH
0x95d2b0392f8e7e54500ebdb0d3d2a2a70625544be98911ca1bc171193335bc871101863042023-09-29 7:09:45157 days 1 hr ago PoolTogether: TWAB Delegator  Contract Creation0 ETH
0x60cf30d1b0108a67a15165dbfa122740b0c69f715613a0fe5fd0b54eada3ecbc1101249402023-09-27 21:04:17158 days 11 hrs ago PoolTogether: TWAB Delegator  Contract Creation0 ETH
0x5229005b66797b605bc7e2162a15fc17f02936f3f934d02cb13cd98085d3d05c1099906022023-09-24 18:26:21161 days 14 hrs ago PoolTogether: TWAB Delegator  Contract Creation0 ETH
0x4ea0a6c274429a3a5bfc3ff93cef590b29de5b909db85109f60b6e6bc2d71b391098875322023-09-22 9:10:41163 days 23 hrs ago PoolTogether: TWAB Delegator  Contract Creation0 ETH
0x213ea316c2444b4b2b1beb3fc5f11e91c77776faa90a506b49efb9b933e13ccd1098843072023-09-22 7:23:11164 days 1 hr ago PoolTogether: TWAB Delegator  Contract Creation0 ETH
0xe3510ff3359a21dc13088b8b2122d35603a7825bb51a03f64e28e445d0bd56ff1098842782023-09-22 7:22:13164 days 1 hr ago PoolTogether: TWAB Delegator  Contract Creation0 ETH
0x9e3971df2171e6285299cf4724e43ef8698ba958ce4b8c4fc317d1ae97a79a8d1098842272023-09-22 7:20:31164 days 1 hr ago PoolTogether: TWAB Delegator  Contract Creation0 ETH
0x1fc8fa46a2703578d69fab27d00534b6cbc808026c4fc10268d6cbd83d6b60b81098841972023-09-22 7:19:31164 days 1 hr ago PoolTogether: TWAB Delegator  Contract Creation0 ETH
0x0b8a7ee3dbc8f388a305022f814bf5bf0f980545ce69d5d414eeba436c370e661098841692023-09-22 7:18:35164 days 1 hr ago PoolTogether: TWAB Delegator  Contract Creation0 ETH
0x37ed0324d26f064dc0a40a0f15eaa32104d3bcfc752e44099b2ff2dd0f1cf02d1098841432023-09-22 7:17:43164 days 1 hr ago PoolTogether: TWAB Delegator  Contract Creation0 ETH
0x3972c099d6da2ea9a5e413ec3d67a36be325e60e13388ee46df0243d30a4a3f01098841192023-09-22 7:16:55164 days 1 hr ago PoolTogether: TWAB Delegator  Contract Creation0 ETH
0xb20ff346fdbafc13be8778d1f373bc578e1b65a6c416521a969c73fef0202adb1098839182023-09-22 7:10:13164 days 1 hr ago PoolTogether: TWAB Delegator  Contract Creation0 ETH
0xf502b689c0bfda104075cb593d2d8f0fdf8486edab7913c95bfe1239e8ea85e11098055562023-09-20 11:38:09165 days 21 hrs ago PoolTogether: TWAB Delegator  Contract Creation0 ETH
0xbe06219548420051b054c7861cb02f506d8ba51db610f27e1e965b53cfa73ab91098045102023-09-20 11:03:17165 days 21 hrs ago PoolTogether: TWAB Delegator  Contract Creation0 ETH
0x598f24176aab175df78746e094d6aa48dfcd1ea1013587d512f15400f8a09d1f1094228562023-09-11 15:01:29174 days 17 hrs ago PoolTogether: TWAB Delegator  Contract Creation0 ETH
0xe9a0142cc32981a94238effc7212187da7aba461f828cb057f219b3a4fa833c11092043992023-09-06 13:39:35179 days 19 hrs ago PoolTogether: TWAB Delegator  Contract Creation0 ETH
0x8e145057f81a287b69a331a662d7672c68035355e3f0c6bc6d7eca26a86e72431091762632023-09-05 22:01:43180 days 10 hrs ago PoolTogether: TWAB Delegator  Contract Creation0 ETH
0x71f5ccdc999ce2c57a07f526c4a083538b2e53b5341a3da024bda6c3979f72971091761462023-09-05 21:57:49180 days 10 hrs ago PoolTogether: TWAB Delegator  Contract Creation0 ETH
0x1bc5770a154f15acfb3a93989a27ae5754becef8e6230125ebe18c538442904a1091747832023-09-05 21:12:23180 days 11 hrs ago PoolTogether: TWAB Delegator  Contract Creation0 ETH
0x610fb31b23ea84bf934b7623a2f6696487f5032d504547f8776e5d85a8e5b89b1091745322023-09-05 21:04:01180 days 11 hrs ago PoolTogether: TWAB Delegator  Contract Creation0 ETH
0x0f82c243d8f2d2a016f05f03ecc635c4bb7fbcc37b893a4cd4f8b6de8bb90aaf1091745172023-09-05 21:03:31180 days 11 hrs ago PoolTogether: TWAB Delegator  Contract Creation0 ETH
0x2ebc9f9e0dbfd87164c39aef77b8f2338ecd0cd927d0360d92cffe01d991a3c91091217182023-09-04 15:43:33181 days 16 hrs ago PoolTogether: TWAB Delegator  Contract Creation0 ETH
[ Download CSV Export 
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.

Contract Source Code Verified (Exact Match)

Contract Name:
TWABDelegator

Compiler Version
v0.8.6+commit.11564f7e

Optimization Enabled:
Yes with 2000 runs

Other Settings:
default evmVersion
File 1 of 20 : TWABDelegator.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.6;

import "@openzeppelin/contracts/proxy/Clones.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@pooltogether/v4-core/contracts/interfaces/ITicket.sol";

import "./Delegation.sol";
import "./LowLevelDelegator.sol";
import "./PermitAndMulticall.sol";

/**
 * @title Delegate chances to win to multiple accounts.
 * @notice This contract allows accounts to easily delegate a portion of their tickets to multiple delegatees.
  The delegatees chance of winning prizes is increased by the delegated amount.
  If a delegator doesn't want to actively manage the delegations, then they can stake on the contract and appoint representatives.
 */
contract TWABDelegator is ERC20, LowLevelDelegator, PermitAndMulticall {
  using Address for address;
  using Clones for address;
  using SafeERC20 for IERC20;

  /* ============ Events ============ */

  /**
   * @notice Emitted when ticket associated with this contract has been set.
   * @param ticket Address of the ticket
   */
  event TicketSet(ITicket indexed ticket);

  /**
   * @notice Emitted when tickets have been staked.
   * @param delegator Address of the delegator
   * @param amount Amount of tickets staked
   */
  event TicketsStaked(address indexed delegator, uint256 amount);

  /**
   * @notice Emitted when tickets have been unstaked.
   * @param delegator Address of the delegator
   * @param recipient Address of the recipient that will receive the tickets
   * @param amount Amount of tickets unstaked
   */
  event TicketsUnstaked(address indexed delegator, address indexed recipient, uint256 amount);

  /**
   * @notice Emitted when a new delegation is created.
   * @param delegator Delegator of the delegation
   * @param slot Slot of the delegation
   * @param lockUntil Timestamp until which the delegation is locked
   * @param delegatee Address of the delegatee
   * @param delegation Address of the delegation that was created
   * @param user Address of the user who created the delegation
   */
  event DelegationCreated(
    address indexed delegator,
    uint256 indexed slot,
    uint96 lockUntil,
    address indexed delegatee,
    Delegation delegation,
    address user
  );

  /**
   * @notice Emitted when a delegatee is updated.
   * @param delegator Address of the delegator
   * @param slot Slot of the delegation
   * @param delegatee Address of the delegatee
   * @param lockUntil Timestamp until which the delegation is locked
   * @param user Address of the user who updated the delegatee
   */
  event DelegateeUpdated(
    address indexed delegator,
    uint256 indexed slot,
    address indexed delegatee,
    uint96 lockUntil,
    address user
  );

  /**
   * @notice Emitted when a delegation is funded.
   * @param delegator Address of the delegator
   * @param slot Slot of the delegation
   * @param amount Amount of tickets that were sent to the delegation
   * @param user Address of the user who funded the delegation
   */
  event DelegationFunded(
    address indexed delegator,
    uint256 indexed slot,
    uint256 amount,
    address indexed user
  );

  /**
   * @notice Emitted when a delegation is funded from the staked amount.
   * @param delegator Address of the delegator
   * @param slot Slot of the delegation
   * @param amount Amount of tickets that were sent to the delegation
   * @param user Address of the user who pulled funds from the delegator stake to the delegation
   */
  event DelegationFundedFromStake(
    address indexed delegator,
    uint256 indexed slot,
    uint256 amount,
    address indexed user
  );

  /**
   * @notice Emitted when an amount of tickets has been withdrawn from a delegation. The tickets are held by this contract and the delegator stake is increased.
   * @param delegator Address of the delegator
   * @param slot Slot of the delegation
   * @param amount Amount of tickets withdrawn
   * @param user Address of the user who withdrew the tickets
   */
  event WithdrewDelegationToStake(
    address indexed delegator,
    uint256 indexed slot,
    uint256 amount,
    address indexed user
  );

  /**
   * @notice Emitted when a delegator withdraws an amount of tickets from a delegation to a specified wallet.
   * @param delegator Address of the delegator
   * @param slot  Slot of the delegation
   * @param amount Amount of tickets withdrawn
   * @param to Recipient address of withdrawn tickets
   */
  event TransferredDelegation(
    address indexed delegator,
    uint256 indexed slot,
    uint256 amount,
    address indexed to
  );

  /**
   * @notice Emitted when a representative is set.
   * @param delegator Address of the delegator
   * @param representative Address of the representative
   * @param set Boolean indicating if the representative was set or unset
   */
  event RepresentativeSet(address indexed delegator, address indexed representative, bool set);

  /* ============ Variables ============ */

  /// @notice Prize pool ticket to which this contract is tied to.
  ITicket public immutable ticket;

  /// @notice Max lock time during which a delegation cannot be updated.
  uint256 public constant MAX_LOCK = 180 days;

  /**
   * @notice Representative elected by the delegator to handle delegation.
   * @dev Representative can only handle delegation and cannot withdraw tickets to their wallet.
   * @dev delegator => representative => bool allowing representative to represent the delegator
   */
  mapping(address => mapping(address => bool)) internal representatives;

  /* ============ Constructor ============ */

  /**
   * @notice Creates a new TWAB Delegator that is bound to the given ticket contract.
   * @param name_ The name for the staked ticket token
   * @param symbol_ The symbol for the staked ticket token
   * @param _ticket Address of the ticket contract
   */
  constructor(
    string memory name_,
    string memory symbol_,
    ITicket _ticket
  ) LowLevelDelegator() ERC20(name_, symbol_) {
    require(address(_ticket) != address(0), "TWABDelegator/tick-not-zero-addr");
    ticket = _ticket;

    emit TicketSet(_ticket);
  }

  /* ============ External Functions ============ */

  /**
   * @notice Stake `_amount` of tickets in this contract.
   * @dev Tickets can be staked on behalf of a `_to` user.
   * @param _to Address to which the stake will be attributed
   * @param _amount Amount of tickets to stake
   */
  function stake(address _to, uint256 _amount) external {
    _requireAmountGtZero(_amount);

    IERC20(ticket).safeTransferFrom(msg.sender, address(this), _amount);
    _mint(_to, _amount);

    emit TicketsStaked(_to, _amount);
  }

  /**
   * @notice Unstake `_amount` of tickets from this contract. Transfers ticket to the passed `_to` address.
   * @dev If delegator has delegated his whole stake, he will first have to withdraw from a delegation to be able to unstake.
   * @param _to Address of the recipient that will receive the tickets
   * @param _amount Amount of tickets to unstake
   */
  function unstake(address _to, uint256 _amount) external {
    _requireRecipientNotZeroAddress(_to);
    _requireAmountGtZero(_amount);

    _burn(msg.sender, _amount);

    IERC20(ticket).safeTransfer(_to, _amount);

    emit TicketsUnstaked(msg.sender, _to, _amount);
  }

  /**
   * @notice Creates a new delegation.
   This will create a new Delegation contract for the given slot and have it delegate its tickets to the given delegatee.
   If a non-zero lock duration is passed, then the delegatee cannot be changed, nor funding withdrawn, until the lock has expired.
   * @dev The `_delegator` and `_slot` params are used to compute the salt of the delegation
   * @param _delegator Address of the delegator that will be able to handle the delegation
   * @param _slot Slot of the delegation
   * @param _delegatee Address of the delegatee
   * @param _lockDuration Duration of time for which the delegation is locked. Must be less than the max duration.
   * @return Returns the address of the Delegation contract that will hold the tickets
   */
  function createDelegation(
    address _delegator,
    uint256 _slot,
    address _delegatee,
    uint96 _lockDuration
  ) external returns (Delegation) {
    _requireDelegatorOrRepresentative(_delegator);
    _requireDelegateeNotZeroAddress(_delegatee);
    _requireLockDuration(_lockDuration);

    uint96 _lockUntil = _computeLockUntil(_lockDuration);

    Delegation _delegation = _createDelegation(
      _computeSalt(_delegator, bytes32(_slot)),
      _lockUntil
    );

    _setDelegateeCall(_delegation, _delegatee);

    emit DelegationCreated(_delegator, _slot, _lockUntil, _delegatee, _delegation, msg.sender);

    return _delegation;
  }

  /**
   * @notice Updates the delegatee and lock duration for a delegation slot.
   * @dev Only callable by the `_delegator` or their representative.
   * @dev Will revert if delegation is still locked.
   * @param _delegator Address of the delegator
   * @param _slot Slot of the delegation
   * @param _delegatee Address of the delegatee
   * @param _lockDuration Duration of time during which the delegatee cannot be changed nor withdrawn
   * @return The address of the Delegation
   */
  function updateDelegatee(
    address _delegator,
    uint256 _slot,
    address _delegatee,
    uint96 _lockDuration
  ) external returns (Delegation) {
    _requireDelegatorOrRepresentative(_delegator);
    _requireDelegateeNotZeroAddress(_delegatee);
    _requireLockDuration(_lockDuration);

    Delegation _delegation = Delegation(_computeAddress(_delegator, _slot));
    _requireDelegationUnlocked(_delegation);

    uint96 _lockUntil = _computeLockUntil(_lockDuration);

    if (_lockDuration > 0) {
      _delegation.setLockUntil(_lockUntil);
    }

    _setDelegateeCall(_delegation, _delegatee);

    emit DelegateeUpdated(_delegator, _slot, _delegatee, _lockUntil, msg.sender);

    return _delegation;
  }

  /**
   * @notice Fund a delegation by transferring tickets from the caller to the delegation.
   * @dev Callable by anyone.
   * @dev Will revert if delegation does not exist.
   * @param _delegator Address of the delegator
   * @param _slot Slot of the delegation
   * @param _amount Amount of tickets to transfer
   * @return The address of the Delegation
   */
  function fundDelegation(
    address _delegator,
    uint256 _slot,
    uint256 _amount
  ) external returns (Delegation) {
    require(_delegator != address(0), "TWABDelegator/dlgtr-not-zero-adr");
    _requireAmountGtZero(_amount);

    Delegation _delegation = Delegation(_computeAddress(_delegator, _slot));
    IERC20(ticket).safeTransferFrom(msg.sender, address(_delegation), _amount);

    emit DelegationFunded(_delegator, _slot, _amount, msg.sender);

    return _delegation;
  }

  /**
   * @notice Fund a delegation using the `_delegator` stake.
   * @dev Callable only by the `_delegator` or a representative.
   * @dev Will revert if delegation does not exist.
   * @dev Will revert if `_amount` is greater than the staked amount.
   * @param _delegator Address of the delegator
   * @param _slot Slot of the delegation
   * @param _amount Amount of tickets to send to the delegation from the staked amount
   * @return The address of the Delegation
   */
  function fundDelegationFromStake(
    address _delegator,
    uint256 _slot,
    uint256 _amount
  ) external returns (Delegation) {
    _requireDelegatorOrRepresentative(_delegator);
    _requireAmountGtZero(_amount);

    Delegation _delegation = Delegation(_computeAddress(_delegator, _slot));

    _burn(_delegator, _amount);

    IERC20(ticket).safeTransfer(address(_delegation), _amount);

    emit DelegationFundedFromStake(_delegator, _slot, _amount, msg.sender);

    return _delegation;
  }

  /**
   * @notice Withdraw tickets from a delegation. The tickets will be held by this contract and the delegator's stake will increase.
   * @dev Only callable by the `_delegator` or a representative.
   * @dev Will send the tickets to this contract and increase the `_delegator` staked amount.
   * @dev Will revert if delegation is still locked.
   * @param _delegator Address of the delegator
   * @param _slot Slot of the delegation
   * @param _amount Amount of tickets to withdraw
   * @return The address of the Delegation
   */
  function withdrawDelegationToStake(
    address _delegator,
    uint256 _slot,
    uint256 _amount
  ) external returns (Delegation) {
    _requireDelegatorOrRepresentative(_delegator);

    Delegation _delegation = Delegation(_computeAddress(_delegator, _slot));

    _transfer(_delegation, address(this), _amount);

    _mint(_delegator, _amount);

    emit WithdrewDelegationToStake(_delegator, _slot, _amount, msg.sender);

    return _delegation;
  }

  /**
   * @notice Withdraw an `_amount` of tickets from a delegation. The delegator is assumed to be the caller.
   * @dev Tickets are sent directly to the passed `_to` address.
   * @dev Will revert if delegation is still locked.
   * @param _slot Slot of the delegation
   * @param _amount Amount to withdraw
   * @param _to Account to transfer the withdrawn tickets to
   * @return The address of the Delegation
   */
  function transferDelegationTo(
    uint256 _slot,
    uint256 _amount,
    address _to
  ) external returns (Delegation) {
    _requireRecipientNotZeroAddress(_to);

    Delegation _delegation = Delegation(_computeAddress(msg.sender, _slot));
    _transfer(_delegation, _to, _amount);

    emit TransferredDelegation(msg.sender, _slot, _amount, _to);

    return _delegation;
  }

  /**
   * @notice Allow an account to set or unset a `_representative` to handle delegation.
   * @dev If `_set` is `true`, `_representative` will be set as representative of `msg.sender`.
   * @dev If `_set` is `false`, `_representative` will be unset as representative of `msg.sender`.
   * @param _representative Address of the representative
   * @param _set Set or unset the representative
   */
  function setRepresentative(address _representative, bool _set) external {
    require(_representative != address(0), "TWABDelegator/rep-not-zero-addr");

    representatives[msg.sender][_representative] = _set;

    emit RepresentativeSet(msg.sender, _representative, _set);
  }

  /**
   * @notice Returns whether or not the given rep is a representative of the delegator.
   * @param _delegator The delegator
   * @param _representative The representative to check for
   * @return True if the rep is a rep, false otherwise
   */
  function isRepresentativeOf(address _delegator, address _representative)
    external
    view
    returns (bool)
  {
    return representatives[_delegator][_representative];
  }

  /**
   * @notice Allows a user to call multiple functions on the same contract.  Useful for EOA who wants to batch transactions.
   * @param _data An array of encoded function calls.  The calls must be abi-encoded calls to this contract.
   * @return The results from each function call
   */
  function multicall(bytes[] calldata _data) external returns (bytes[] memory) {
    return _multicall(_data);
  }

  /**
   * @notice Alow a user to approve ticket and run various calls in one transaction.
   * @param _amount Amount of tickets to approve
   * @param _permitSignature Permit signature
   * @param _data Datas to call with `functionDelegateCall`
   */
  function permitAndMulticall(
    uint256 _amount,
    Signature calldata _permitSignature,
    bytes[] calldata _data
  ) external {
    _permitAndMulticall(IERC20Permit(address(ticket)), _amount, _permitSignature, _data);
  }

  /**
   * @notice Allows the caller to easily get the details for a delegation.
   * @param _delegator The delegator address
   * @param _slot The delegation slot they are using
   * @return delegation The address that holds tickets for the delegation
   * @return delegatee The address that tickets are being delegated to
   * @return balance The balance of tickets in the delegation
   * @return lockUntil The timestamp at which the delegation unlocks
   * @return wasCreated Whether or not the delegation has been created
   */
  function getDelegation(address _delegator, uint256 _slot)
    external
    view
    returns (
      Delegation delegation,
      address delegatee,
      uint256 balance,
      uint256 lockUntil,
      bool wasCreated
    )
  {
    delegation = Delegation(_computeAddress(_delegator, _slot));
    wasCreated = address(delegation).isContract();
    delegatee = ticket.delegateOf(address(delegation));
    balance = ticket.balanceOf(address(delegation));

    if (wasCreated) {
      lockUntil = delegation.lockUntil();
    }
  }

  /**
   * @notice Computes the address of the delegation for the delegator + slot combination.
   * @param _delegator The user who is delegating tickets
   * @param _slot The delegation slot
   * @return The address of the delegation.  This is the address that holds the balance of tickets.
   */
  function computeDelegationAddress(address _delegator, uint256 _slot)
    external
    view
    returns (address)
  {
    return _computeAddress(_delegator, _slot);
  }

  /**
   * @notice Returns the ERC20 token decimals.
   * @dev This value is equal to the decimals of the ticket being delegated.
   * @return ERC20 token decimals
   */
  function decimals() public view virtual override returns (uint8) {
    return ERC20(address(ticket)).decimals();
  }

  /* ============ Internal Functions ============ */

  /**
   * @notice Computes the address of a delegation contract using the delegator and slot as a salt.
   The contract is a clone, also known as minimal proxy contract.
   * @param _delegator Address of the delegator
   * @param _slot Slot of the delegation
   * @return Address at which the delegation contract will be deployed
   */
  function _computeAddress(address _delegator, uint256 _slot) internal view returns (address) {
    return _computeAddress(_computeSalt(_delegator, bytes32(_slot)));
  }

  /**
   * @notice Computes the timestamp at which the delegation unlocks, after which the delegatee can be changed and tickets withdrawn.
   * @param _lockDuration The duration of the lock
   * @return The lock expiration timestamp
   */
  function _computeLockUntil(uint96 _lockDuration) internal view returns (uint96) {
    unchecked {
      return uint96(block.timestamp) + _lockDuration;
    }
  }

  /**
   * @notice Delegates tickets from the `_delegation` contract to the `_delegatee` address.
   * @param _delegation Address of the delegation contract
   * @param _delegatee Address of the delegatee
   */
  function _setDelegateeCall(Delegation _delegation, address _delegatee) internal {
    bytes4 _selector = ticket.delegate.selector;
    bytes memory _data = abi.encodeWithSelector(_selector, _delegatee);

    _executeCall(_delegation, _data);
  }

  /**
   * @notice Tranfers tickets from the Delegation contract to the `_to` address.
   * @param _delegation Address of the delegation contract
   * @param _to Address of the recipient
   * @param _amount Amount of tickets to transfer
   */
  function _transferCall(
    Delegation _delegation,
    address _to,
    uint256 _amount
  ) internal {
    bytes4 _selector = ticket.transfer.selector;
    bytes memory _data = abi.encodeWithSelector(_selector, _to, _amount);

    _executeCall(_delegation, _data);
  }

  /**
   * @notice Execute a function call on the delegation contract.
   * @param _delegation Address of the delegation contract
   * @param _data The call data that will be executed
   * @return The return datas from the calls
   */
  function _executeCall(Delegation _delegation, bytes memory _data)
    internal
    returns (bytes[] memory)
  {
    Delegation.Call[] memory _calls = new Delegation.Call[](1);
    _calls[0] = Delegation.Call({ to: address(ticket), data: _data });

    return _delegation.executeCalls(_calls);
  }

  /**
   * @notice Transfers tickets from a delegation contract to `_to`.
   * @param _delegation Address of the delegation contract
   * @param _to Address of the recipient
   * @param _amount Amount of tickets to transfer
   */
  function _transfer(
    Delegation _delegation,
    address _to,
    uint256 _amount
  ) internal {
    _requireAmountGtZero(_amount);
    _requireDelegationUnlocked(_delegation);

    _transferCall(_delegation, _to, _amount);
  }

  /* ============ Modifier/Require Functions ============ */

  /**
   * @notice Require to only allow the delegator or representative to call a function.
   * @param _delegator Address of the delegator
   */
  function _requireDelegatorOrRepresentative(address _delegator) internal view {
    require(
      _delegator == msg.sender || representatives[_delegator][msg.sender],
      "TWABDelegator/not-dlgtr-or-rep"
    );
  }

  /**
   * @notice Require to verify that `_delegatee` is not address zero.
   * @param _delegatee Address of the delegatee
   */
  function _requireDelegateeNotZeroAddress(address _delegatee) internal pure {
    require(_delegatee != address(0), "TWABDelegator/dlgt-not-zero-addr");
  }

  /**
   * @notice Require to verify that `_amount` is greater than 0.
   * @param _amount Amount to check
   */
  function _requireAmountGtZero(uint256 _amount) internal pure {
    require(_amount > 0, "TWABDelegator/amount-gt-zero");
  }

  /**
   * @notice Require to verify that `_to` is not address zero.
   * @param _to Address to check
   */
  function _requireRecipientNotZeroAddress(address _to) internal pure {
    require(_to != address(0), "TWABDelegator/to-not-zero-addr");
  }

  /**
   * @notice Require to verify if a `_delegation` is locked.
   * @param _delegation Delegation to check
   */
  function _requireDelegationUnlocked(Delegation _delegation) internal view {
    require(block.timestamp >= _delegation.lockUntil(), "TWABDelegator/delegation-locked");
  }

  /**
   * @notice Require to verify that a `_lockDuration` does not exceed the maximum lock duration.
   * @param _lockDuration Lock duration to check
   */
  function _requireLockDuration(uint256 _lockDuration) internal pure {
    require(_lockDuration <= MAX_LOCK, "TWABDelegator/lock-too-long");
  }
}

File 2 of 20 : Clones.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (proxy/Clones.sol)

pragma solidity ^0.8.0;

/**
 * @dev https://eips.ethereum.org/EIPS/eip-1167[EIP 1167] is a standard for
 * deploying minimal proxy contracts, also known as "clones".
 *
 * > To simply and cheaply clone contract functionality in an immutable way, this standard specifies
 * > a minimal bytecode implementation that delegates all calls to a known, fixed address.
 *
 * The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2`
 * (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the
 * deterministic method.
 *
 * _Available since v3.4._
 */
library Clones {
    /**
     * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
     *
     * This function uses the create opcode, which should never revert.
     */
    function clone(address implementation) internal returns (address instance) {
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
            mstore(add(ptr, 0x14), shl(0x60, implementation))
            mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
            instance := create(0, ptr, 0x37)
        }
        require(instance != address(0), "ERC1167: create failed");
    }

    /**
     * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
     *
     * This function uses the create2 opcode and a `salt` to deterministically deploy
     * the clone. Using the same `implementation` and `salt` multiple time will revert, since
     * the clones cannot be deployed twice at the same address.
     */
    function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) {
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
            mstore(add(ptr, 0x14), shl(0x60, implementation))
            mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
            instance := create2(0, ptr, 0x37, salt)
        }
        require(instance != address(0), "ERC1167: create2 failed");
    }

    /**
     * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
     */
    function predictDeterministicAddress(
        address implementation,
        bytes32 salt,
        address deployer
    ) internal pure returns (address predicted) {
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
            mstore(add(ptr, 0x14), shl(0x60, implementation))
            mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf3ff00000000000000000000000000000000)
            mstore(add(ptr, 0x38), shl(0x60, deployer))
            mstore(add(ptr, 0x4c), salt)
            mstore(add(ptr, 0x6c), keccak256(ptr, 0x37))
            predicted := keccak256(add(ptr, 0x37), 0x55)
        }
    }

    /**
     * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
     */
    function predictDeterministicAddress(address implementation, bytes32 salt)
        internal
        view
        returns (address predicted)
    {
        return predictDeterministicAddress(implementation, salt, address(this));
    }
}

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

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

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

pragma solidity ^0.8.0;

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

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

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

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;

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

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

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

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

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

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

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

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

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

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

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

        return true;
    }

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

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

        return true;
    }

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

        _beforeTokenTransfer(sender, recipient, amount);

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

        emit Transfer(sender, recipient, amount);

        _afterTokenTransfer(sender, recipient, amount);
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

pragma solidity ^0.8.0;

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

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

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

    function safeTransferFrom(
        IERC20 token,
        address from,
        address to,
        uint256 value
    ) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

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

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

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

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

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

File 6 of 20 : ITicket.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.6;

import "../libraries/TwabLib.sol";
import "./IControlledToken.sol";

interface ITicket is IControlledToken {
    /**
     * @notice A struct containing details for an Account.
     * @param balance The current balance for an Account.
     * @param nextTwabIndex The next available index to store a new twab.
     * @param cardinality The number of recorded twabs (plus one!).
     */
    struct AccountDetails {
        uint224 balance;
        uint16 nextTwabIndex;
        uint16 cardinality;
    }

    /**
     * @notice Combines account details with their twab history.
     * @param details The account details.
     * @param twabs The history of twabs for this account.
     */
    struct Account {
        AccountDetails details;
        ObservationLib.Observation[65535] twabs;
    }

    /**
     * @notice Emitted when TWAB balance has been delegated to another user.
     * @param delegator Address of the delegator.
     * @param delegate Address of the delegate.
     */
    event Delegated(address indexed delegator, address indexed delegate);

    /**
     * @notice Emitted when ticket is initialized.
     * @param name Ticket name (eg: PoolTogether Dai Ticket (Compound)).
     * @param symbol Ticket symbol (eg: PcDAI).
     * @param decimals Ticket decimals.
     * @param controller Token controller address.
     */
    event TicketInitialized(string name, string symbol, uint8 decimals, address indexed controller);

    /**
     * @notice Emitted when a new TWAB has been recorded.
     * @param delegate The recipient of the ticket power (may be the same as the user).
     * @param newTwab Updated TWAB of a ticket holder after a successful TWAB recording.
     */
    event NewUserTwab(
        address indexed delegate,
        ObservationLib.Observation newTwab
    );

    /**
     * @notice Emitted when a new total supply TWAB has been recorded.
     * @param newTotalSupplyTwab Updated TWAB of tickets total supply after a successful total supply TWAB recording.
     */
    event NewTotalSupplyTwab(ObservationLib.Observation newTotalSupplyTwab);

    /**
     * @notice Retrieves the address of the delegate to whom `user` has delegated their tickets.
     * @dev Address of the delegate will be the zero address if `user` has not delegated their tickets.
     * @param user Address of the delegator.
     * @return Address of the delegate.
     */
    function delegateOf(address user) external view returns (address);

    /**
    * @notice Delegate time-weighted average balances to an alternative address.
    * @dev    Transfers (including mints) trigger the storage of a TWAB in delegate(s) account, instead of the
              targetted sender and/or recipient address(s).
    * @dev    To reset the delegate, pass the zero address (0x000.000) as `to` parameter.
    * @dev Current delegate address should be different from the new delegate address `to`.
    * @param  to Recipient of delegated TWAB.
    */
    function delegate(address to) external;

    /**
     * @notice Allows the controller to delegate on a users behalf.
     * @param user The user for whom to delegate
     * @param delegate The new delegate
     */
    function controllerDelegateFor(address user, address delegate) external;

    /**
     * @notice Allows a user to delegate via signature
     * @param user The user who is delegating
     * @param delegate The new delegate
     * @param deadline The timestamp by which this must be submitted
     * @param v The v portion of the ECDSA sig
     * @param r The r portion of the ECDSA sig
     * @param s The s portion of the ECDSA sig
     */
    function delegateWithSignature(
        address user,
        address delegate,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @notice Gets a users twab context.  This is a struct with their balance, next twab index, and cardinality.
     * @param user The user for whom to fetch the TWAB context.
     * @return The TWAB context, which includes { balance, nextTwabIndex, cardinality }
     */
    function getAccountDetails(address user) external view returns (TwabLib.AccountDetails memory);

    /**
     * @notice Gets the TWAB at a specific index for a user.
     * @param user The user for whom to fetch the TWAB.
     * @param index The index of the TWAB to fetch.
     * @return The TWAB, which includes the twab amount and the timestamp.
     */
    function getTwab(address user, uint16 index)
        external
        view
        returns (ObservationLib.Observation memory);

    /**
     * @notice Retrieves `user` TWAB balance.
     * @param user Address of the user whose TWAB is being fetched.
     * @param timestamp Timestamp at which we want to retrieve the TWAB balance.
     * @return The TWAB balance at the given timestamp.
     */
    function getBalanceAt(address user, uint64 timestamp) external view returns (uint256);

    /**
     * @notice Retrieves `user` TWAB balances.
     * @param user Address of the user whose TWABs are being fetched.
     * @param timestamps Timestamps range at which we want to retrieve the TWAB balances.
     * @return `user` TWAB balances.
     */
    function getBalancesAt(address user, uint64[] calldata timestamps)
        external
        view
        returns (uint256[] memory);

    /**
     * @notice Retrieves the average balance held by a user for a given time frame.
     * @param user The user whose balance is checked.
     * @param startTime The start time of the time frame.
     * @param endTime The end time of the time frame.
     * @return The average balance that the user held during the time frame.
     */
    function getAverageBalanceBetween(
        address user,
        uint64 startTime,
        uint64 endTime
    ) external view returns (uint256);

    /**
     * @notice Retrieves the average balances held by a user for a given time frame.
     * @param user The user whose balance is checked.
     * @param startTimes The start time of the time frame.
     * @param endTimes The end time of the time frame.
     * @return The average balance that the user held during the time frame.
     */
    function getAverageBalancesBetween(
        address user,
        uint64[] calldata startTimes,
        uint64[] calldata endTimes
    ) external view returns (uint256[] memory);

    /**
     * @notice Retrieves the total supply TWAB balance at the given timestamp.
     * @param timestamp Timestamp at which we want to retrieve the total supply TWAB balance.
     * @return The total supply TWAB balance at the given timestamp.
     */
    function getTotalSupplyAt(uint64 timestamp) external view returns (uint256);

    /**
     * @notice Retrieves the total supply TWAB balance between the given timestamps range.
     * @param timestamps Timestamps range at which we want to retrieve the total supply TWAB balance.
     * @return Total supply TWAB balances.
     */
    function getTotalSuppliesAt(uint64[] calldata timestamps)
        external
        view
        returns (uint256[] memory);

    /**
     * @notice Retrieves the average total supply balance for a set of given time frames.
     * @param startTimes Array of start times.
     * @param endTimes Array of end times.
     * @return The average total supplies held during the time frame.
     */
    function getAverageTotalSuppliesBetween(
        uint64[] calldata startTimes,
        uint64[] calldata endTimes
    ) external view returns (uint256[] memory);
}

File 7 of 20 : Delegation.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.6;

/**
 * @title Contract instantiated via CREATE2 to handle a Delegation by a delegator to a delegatee.
 * @notice A Delegation allows his owner to execute calls on behalf of the contract.
 * @dev This contract is intended to be counterfactually instantiated via CREATE2 through the LowLevelDelegator contract.
 * @dev This contract will hold tickets that will be delegated to a chosen delegatee.
 */
contract Delegation {
  /**
   * @notice A structure to define arbitrary contract calls.
   * @param to The address to call
   * @param data The call data
   */
  struct Call {
    address to;
    bytes data;
  }

  /// @notice Contract owner.
  address private _owner;

  /// @notice Timestamp until which the delegation is locked.
  uint96 public lockUntil;

  /**
   * @notice Initializes the delegation.
   * @param _lockUntil Timestamp until which the delegation is locked
   */
  function initialize(uint96 _lockUntil) external {
    require(_owner == address(0), "Delegation/already-init");
    _owner = msg.sender;
    lockUntil = _lockUntil;
  }

  /**
   * @notice Executes calls on behalf of this contract.
   * @param calls The array of calls to be executed
   * @return An array of the return values for each of the calls
   */
  function executeCalls(Call[] calldata calls) external onlyOwner returns (bytes[] memory) {
    uint256 _callsLength = calls.length;
    bytes[] memory response = new bytes[](_callsLength);
    Call memory call;

    for (uint256 i; i < _callsLength; i++) {
      call = calls[i];
      response[i] = _executeCall(call.to, call.data);
    }

    return response;
  }

  /**
   * @notice Set the timestamp until which the delegation is locked.
   * @param _lockUntil The timestamp until which the delegation is locked
   */
  function setLockUntil(uint96 _lockUntil) external onlyOwner {
    lockUntil = _lockUntil;
  }

  /**
   * @notice Executes a call to another contract.
   * @param to The address to call
   * @param data The call data
   * @return The return data from the call
   */
  function _executeCall(address to, bytes memory data) internal returns (bytes memory) {
    (bool succeeded, bytes memory returnValue) = to.call{ value: 0 }(data);
    require(succeeded, string(returnValue));
    return returnValue;
  }

  /// @notice Modifier to only allow the contract owner to call a function
  modifier onlyOwner() {
    require(msg.sender == _owner, "Delegation/only-owner");
    _;
  }
}

File 8 of 20 : LowLevelDelegator.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.6;

import "@openzeppelin/contracts/proxy/Clones.sol";

import "./Delegation.sol";

/// @title The LowLevelDelegator allows users to create delegations very cheaply.
contract LowLevelDelegator {
  using Clones for address;

  /// @notice The instance to which all proxies will point.
  Delegation public delegationInstance;

  /// @notice Contract constructor.
  constructor() {
    delegationInstance = new Delegation();
    delegationInstance.initialize(uint96(0));
  }

  /**
   * @notice Creates a clone of the delegation.
   * @param _salt Random number used to deterministically deploy the clone
   * @param _lockUntil Timestamp until which the delegation is locked
   * @return The newly created delegation
   */
  function _createDelegation(bytes32 _salt, uint96 _lockUntil) internal returns (Delegation) {
    Delegation _delegation = Delegation(address(delegationInstance).cloneDeterministic(_salt));
    _delegation.initialize(_lockUntil);
    return _delegation;
  }

  /**
   * @notice Computes the address of a clone, also known as minimal proxy contract.
   * @param _salt Random number used to compute the address
   * @return Address at which the clone will be deployed
   */
  function _computeAddress(bytes32 _salt) internal view returns (address) {
    return address(delegationInstance).predictDeterministicAddress(_salt, address(this));
  }

  /**
   * @notice Computes salt used to deterministically deploy a clone.
   * @param _delegator Address of the delegator
   * @param _slot Slot of the delegation
   * @return Salt used to deterministically deploy a clone.
   */
  function _computeSalt(address _delegator, bytes32 _slot) internal pure returns (bytes32) {
    return keccak256(abi.encodePacked(_delegator, _slot));
  }
}

File 9 of 20 : PermitAndMulticall.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.6;

import "@openzeppelin/contracts/token/ERC20/extensions/draft-IERC20Permit.sol";
import "@openzeppelin/contracts/utils/Address.sol";

/**
 * @notice Allows a user to permit token spend and then call multiple functions on a contract.
 */
contract PermitAndMulticall {
  /**
   * @notice Secp256k1 signature values.
   * @param deadline Timestamp at which the signature expires
   * @param v `v` portion of the signature
   * @param r `r` portion of the signature
   * @param s `s` portion of the signature
   */
  struct Signature {
    uint256 deadline;
    uint8 v;
    bytes32 r;
    bytes32 s;
  }

  /**
   * @notice Allows a user to call multiple functions on the same contract.  Useful for EOA who want to batch transactions.
   * @param _data An array of encoded function calls.  The calls must be abi-encoded calls to this contract.
   * @return The results from each function call
   */
  function _multicall(bytes[] calldata _data) internal virtual returns (bytes[] memory) {
    uint256 _dataLength = _data.length;
    bytes[] memory results = new bytes[](_dataLength);

    for (uint256 i; i < _dataLength; i++) {
      results[i] = Address.functionDelegateCall(address(this), _data[i]);
    }

    return results;
  }

  /**
   * @notice Allow a user to approve an ERC20 token and run various calls in one transaction.
   * @param _permitToken Address of the ERC20 token
   * @param _amount Amount of tickets to approve
   * @param _permitSignature Permit signature
   * @param _data Datas to call with `functionDelegateCall`
   */
  function _permitAndMulticall(
    IERC20Permit _permitToken,
    uint256 _amount,
    Signature calldata _permitSignature,
    bytes[] calldata _data
  ) internal {
    _permitToken.permit(
      msg.sender,
      address(this),
      _amount,
      _permitSignature.deadline,
      _permitSignature.v,
      _permitSignature.r,
      _permitSignature.s
    );

    _multicall(_data);
  }
}

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

pragma solidity ^0.8.0;

import "../IERC20.sol";

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

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

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

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

pragma solidity ^0.8.0;

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

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

File 12 of 20 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Address.sol)

pragma solidity ^0.8.0;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize, which returns 0 for contracts in
        // construction, since the code is only stored at the end of the
        // constructor execution.

        uint256 size;
        assembly {
            size := extcodesize(account)
        }
        return size > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://diligence.consensys.net/posts/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.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

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

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

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

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

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

        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");

        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(isContract(target), "Address: delegate call to non-contract");

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

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

                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

File 13 of 20 : TwabLib.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.6;

import "./ExtendedSafeCastLib.sol";
import "./OverflowSafeComparatorLib.sol";
import "./RingBufferLib.sol";
import "./ObservationLib.sol";

/**
  * @title  PoolTogether V4 TwabLib (Library)
  * @author PoolTogether Inc Team
  * @dev    Time-Weighted Average Balance Library for ERC20 tokens.
  * @notice This TwabLib adds on-chain historical lookups to a user(s) time-weighted average balance.
            Each user is mapped to an Account struct containing the TWAB history (ring bufffer) and
            ring buffer parameters. Every token.transfer() creates a new TWAB checkpoint. The new TWAB
            checkpoint is stored in the circular ring buffer, as either a new checkpoint or rewriting
            a previous checkpoint with new parameters. The TwabLib (using existing blocktimes 1block/15sec)
            guarantees minimum 7.4 years of search history.
 */
library TwabLib {
    using OverflowSafeComparatorLib for uint32;
    using ExtendedSafeCastLib for uint256;

    /**
      * @notice Sets max ring buffer length in the Account.twabs Observation list.
                As users transfer/mint/burn tickets new Observation checkpoints are
                recorded. The current max cardinality guarantees a six month minimum,
                of historical accurate lookups with current estimates of 1 new block
                every 15 seconds - the of course contain a transfer to trigger an
                observation write to storage.
      * @dev    The user Account.AccountDetails.cardinality parameter can NOT exceed
                the max cardinality variable. Preventing "corrupted" ring buffer lookup
                pointers and new observation checkpoints.

                The MAX_CARDINALITY in fact guarantees at least 7.4 years of records:
                If 14 = block time in seconds
                (2**24) * 14 = 234881024 seconds of history
                234881024 / (365 * 24 * 60 * 60) ~= 7.44 years
    */
    uint24 public constant MAX_CARDINALITY = 16777215; // 2**24

    /** @notice Struct ring buffer parameters for single user Account
      * @param balance       Current balance for an Account
      * @param nextTwabIndex Next uninitialized or updatable ring buffer checkpoint storage slot
      * @param cardinality   Current total "initialized" ring buffer checkpoints for single user AccountDetails.
                             Used to set initial boundary conditions for an efficient binary search.
    */
    struct AccountDetails {
        uint208 balance;
        uint24 nextTwabIndex;
        uint24 cardinality;
    }

    /// @notice Combines account details with their twab history
    /// @param details The account details
    /// @param twabs The history of twabs for this account
    struct Account {
        AccountDetails details;
        ObservationLib.Observation[MAX_CARDINALITY] twabs;
    }

    /// @notice Increases an account's balance and records a new twab.
    /// @param _account The account whose balance will be increased
    /// @param _amount The amount to increase the balance by
    /// @param _currentTime The current time
    /// @return accountDetails The new AccountDetails
    /// @return twab The user's latest TWAB
    /// @return isNew Whether the TWAB is new
    function increaseBalance(
        Account storage _account,
        uint208 _amount,
        uint32 _currentTime
    )
        internal
        returns (
            AccountDetails memory accountDetails,
            ObservationLib.Observation memory twab,
            bool isNew
        )
    {
        AccountDetails memory _accountDetails = _account.details;
        (accountDetails, twab, isNew) = _nextTwab(_account.twabs, _accountDetails, _currentTime);
        accountDetails.balance = _accountDetails.balance + _amount;
    }

    /** @notice Calculates the next TWAB checkpoint for an account with a decreasing balance.
     * @dev    With Account struct and amount decreasing calculates the next TWAB observable checkpoint.
     * @param _account        Account whose balance will be decreased
     * @param _amount         Amount to decrease the balance by
     * @param _revertMessage  Revert message for insufficient balance
     * @return accountDetails Updated Account.details struct
     * @return twab           TWAB observation (with decreasing average)
     * @return isNew          Whether TWAB is new or calling twice in the same block
     */
    function decreaseBalance(
        Account storage _account,
        uint208 _amount,
        string memory _revertMessage,
        uint32 _currentTime
    )
        internal
        returns (
            AccountDetails memory accountDetails,
            ObservationLib.Observation memory twab,
            bool isNew
        )
    {
        AccountDetails memory _accountDetails = _account.details;

        require(_accountDetails.balance >= _amount, _revertMessage);

        (accountDetails, twab, isNew) = _nextTwab(_account.twabs, _accountDetails, _currentTime);
        unchecked {
            accountDetails.balance -= _amount;
        }
    }

    /** @notice Calculates the average balance held by a user for a given time frame.
      * @dev    Finds the average balance between start and end timestamp epochs.
                Validates the supplied end time is within the range of elapsed time i.e. less then timestamp of now.
      * @param _twabs          Individual user Observation recorded checkpoints passed as storage pointer
      * @param _accountDetails User AccountDetails struct loaded in memory
      * @param _startTime      Start of timestamp range as an epoch
      * @param _endTime        End of timestamp range as an epoch
      * @param _currentTime    Block.timestamp
      * @return Average balance of user held between epoch timestamps start and end
    */
    function getAverageBalanceBetween(
        ObservationLib.Observation[MAX_CARDINALITY] storage _twabs,
        AccountDetails memory _accountDetails,
        uint32 _startTime,
        uint32 _endTime,
        uint32 _currentTime
    ) internal view returns (uint256) {
        uint32 endTime = _endTime > _currentTime ? _currentTime : _endTime;

        return
            _getAverageBalanceBetween(_twabs, _accountDetails, _startTime, endTime, _currentTime);
    }

    /// @notice Retrieves the oldest TWAB
    /// @param _twabs The storage array of twabs
    /// @param _accountDetails The TWAB account details
    /// @return index The index of the oldest TWAB in the twabs array
    /// @return twab The oldest TWAB
    function oldestTwab(
        ObservationLib.Observation[MAX_CARDINALITY] storage _twabs,
        AccountDetails memory _accountDetails
    ) internal view returns (uint24 index, ObservationLib.Observation memory twab) {
        index = _accountDetails.nextTwabIndex;
        twab = _twabs[index];

        // If the TWAB is not initialized we go to the beginning of the TWAB circular buffer at index 0
        if (twab.timestamp == 0) {
            index = 0;
            twab = _twabs[0];
        }
    }

    /// @notice Retrieves the newest TWAB
    /// @param _twabs The storage array of twabs
    /// @param _accountDetails The TWAB account details
    /// @return index The index of the newest TWAB in the twabs array
    /// @return twab The newest TWAB
    function newestTwab(
        ObservationLib.Observation[MAX_CARDINALITY] storage _twabs,
        AccountDetails memory _accountDetails
    ) internal view returns (uint24 index, ObservationLib.Observation memory twab) {
        index = uint24(RingBufferLib.newestIndex(_accountDetails.nextTwabIndex, MAX_CARDINALITY));
        twab = _twabs[index];
    }

    /// @notice Retrieves amount at `_targetTime` timestamp
    /// @param _twabs List of TWABs to search through.
    /// @param _accountDetails Accounts details
    /// @param _targetTime Timestamp at which the reserved TWAB should be for.
    /// @return uint256 TWAB amount at `_targetTime`.
    function getBalanceAt(
        ObservationLib.Observation[MAX_CARDINALITY] storage _twabs,
        AccountDetails memory _accountDetails,
        uint32 _targetTime,
        uint32 _currentTime
    ) internal view returns (uint256) {
        uint32 timeToTarget = _targetTime > _currentTime ? _currentTime : _targetTime;
        return _getBalanceAt(_twabs, _accountDetails, timeToTarget, _currentTime);
    }

    /// @notice Calculates the average balance held by a user for a given time frame.
    /// @param _startTime The start time of the time frame.
    /// @param _endTime The end time of the time frame.
    /// @return The average balance that the user held during the time frame.
    function _getAverageBalanceBetween(
        ObservationLib.Observation[MAX_CARDINALITY] storage _twabs,
        AccountDetails memory _accountDetails,
        uint32 _startTime,
        uint32 _endTime,
        uint32 _currentTime
    ) private view returns (uint256) {
        (uint24 oldestTwabIndex, ObservationLib.Observation memory oldTwab) = oldestTwab(
            _twabs,
            _accountDetails
        );

        (uint24 newestTwabIndex, ObservationLib.Observation memory newTwab) = newestTwab(
            _twabs,
            _accountDetails
        );

        ObservationLib.Observation memory startTwab = _calculateTwab(
            _twabs,
            _accountDetails,
            newTwab,
            oldTwab,
            newestTwabIndex,
            oldestTwabIndex,
            _startTime,
            _currentTime
        );

        ObservationLib.Observation memory endTwab = _calculateTwab(
            _twabs,
            _accountDetails,
            newTwab,
            oldTwab,
            newestTwabIndex,
            oldestTwabIndex,
            _endTime,
            _currentTime
        );

        // Difference in amount / time
        return (endTwab.amount - startTwab.amount) / OverflowSafeComparatorLib.checkedSub(endTwab.timestamp, startTwab.timestamp, _currentTime);
    }

    /** @notice Searches TWAB history and calculate the difference between amount(s)/timestamp(s) to return average balance
                between the Observations closes to the supplied targetTime.
      * @param _twabs          Individual user Observation recorded checkpoints passed as storage pointer
      * @param _accountDetails User AccountDetails struct loaded in memory
      * @param _targetTime     Target timestamp to filter Observations in the ring buffer binary search
      * @param _currentTime    Block.timestamp
      * @return uint256 Time-weighted average amount between two closest observations.
    */
    function _getBalanceAt(
        ObservationLib.Observation[MAX_CARDINALITY] storage _twabs,
        AccountDetails memory _accountDetails,
        uint32 _targetTime,
        uint32 _currentTime
    ) private view returns (uint256) {
        uint24 newestTwabIndex;
        ObservationLib.Observation memory afterOrAt;
        ObservationLib.Observation memory beforeOrAt;
        (newestTwabIndex, beforeOrAt) = newestTwab(_twabs, _accountDetails);

        // If `_targetTime` is chronologically after the newest TWAB, we can simply return the current balance
        if (beforeOrAt.timestamp.lte(_targetTime, _currentTime)) {
            return _accountDetails.balance;
        }

        uint24 oldestTwabIndex;
        // Now, set before to the oldest TWAB
        (oldestTwabIndex, beforeOrAt) = oldestTwab(_twabs, _accountDetails);

        // If `_targetTime` is chronologically before the oldest TWAB, we can early return
        if (_targetTime.lt(beforeOrAt.timestamp, _currentTime)) {
            return 0;
        }

        // Otherwise, we perform the `binarySearch`
        (beforeOrAt, afterOrAt) = ObservationLib.binarySearch(
            _twabs,
            newestTwabIndex,
            oldestTwabIndex,
            _targetTime,
            _accountDetails.cardinality,
            _currentTime
        );

        // Sum the difference in amounts and divide by the difference in timestamps.
        // The time-weighted average balance uses time measured between two epoch timestamps as
        // a constaint on the measurement when calculating the time weighted average balance.
        return
            (afterOrAt.amount - beforeOrAt.amount) / OverflowSafeComparatorLib.checkedSub(afterOrAt.timestamp, beforeOrAt.timestamp, _currentTime);
    }

    /** @notice Calculates a user TWAB for a target timestamp using the historical TWAB records.
                The balance is linearly interpolated: amount differences / timestamp differences
                using the simple (after.amount - before.amount / end.timestamp - start.timestamp) formula.
    /** @dev    Binary search in _calculateTwab fails when searching out of bounds. Thus, before
                searching we exclude target timestamps out of range of newest/oldest TWAB(s).
                IF a search is before or after the range we "extrapolate" a Observation from the expected state.
      * @param _twabs           Individual user Observation recorded checkpoints passed as storage pointer
      * @param _accountDetails  User AccountDetails struct loaded in memory
      * @param _newestTwab      Newest TWAB in history (end of ring buffer)
      * @param _oldestTwab      Olderst TWAB in history (end of ring buffer)
      * @param _newestTwabIndex Pointer in ring buffer to newest TWAB
      * @param _oldestTwabIndex Pointer in ring buffer to oldest TWAB
      * @param _targetTimestamp Epoch timestamp to calculate for time (T) in the TWAB
      * @param _time            Block.timestamp
      * @return accountDetails Updated Account.details struct
    */
    function _calculateTwab(
        ObservationLib.Observation[MAX_CARDINALITY] storage _twabs,
        AccountDetails memory _accountDetails,
        ObservationLib.Observation memory _newestTwab,
        ObservationLib.Observation memory _oldestTwab,
        uint24 _newestTwabIndex,
        uint24 _oldestTwabIndex,
        uint32 _targetTimestamp,
        uint32 _time
    ) private view returns (ObservationLib.Observation memory) {
        // If `_targetTimestamp` is chronologically after the newest TWAB, we extrapolate a new one
        if (_newestTwab.timestamp.lt(_targetTimestamp, _time)) {
            return _computeNextTwab(_newestTwab, _accountDetails.balance, _targetTimestamp);
        }

        if (_newestTwab.timestamp == _targetTimestamp) {
            return _newestTwab;
        }

        if (_oldestTwab.timestamp == _targetTimestamp) {
            return _oldestTwab;
        }

        // If `_targetTimestamp` is chronologically before the oldest TWAB, we create a zero twab
        if (_targetTimestamp.lt(_oldestTwab.timestamp, _time)) {
            return ObservationLib.Observation({ amount: 0, timestamp: _targetTimestamp });
        }

        // Otherwise, both timestamps must be surrounded by twabs.
        (
            ObservationLib.Observation memory beforeOrAtStart,
            ObservationLib.Observation memory afterOrAtStart
        ) = ObservationLib.binarySearch(
                _twabs,
                _newestTwabIndex,
                _oldestTwabIndex,
                _targetTimestamp,
                _accountDetails.cardinality,
                _time
            );

        uint224 heldBalance = (afterOrAtStart.amount - beforeOrAtStart.amount) /
            OverflowSafeComparatorLib.checkedSub(afterOrAtStart.timestamp, beforeOrAtStart.timestamp, _time);

        return _computeNextTwab(beforeOrAtStart, heldBalance, _targetTimestamp);
    }

    /**
     * @notice Calculates the next TWAB using the newestTwab and updated balance.
     * @dev    Storage of the TWAB obersation is managed by the calling function and not _computeNextTwab.
     * @param _currentTwab    Newest Observation in the Account.twabs list
     * @param _currentBalance User balance at time of most recent (newest) checkpoint write
     * @param _time           Current block.timestamp
     * @return TWAB Observation
     */
    function _computeNextTwab(
        ObservationLib.Observation memory _currentTwab,
        uint224 _currentBalance,
        uint32 _time
    ) private pure returns (ObservationLib.Observation memory) {
        // New twab amount = last twab amount (or zero) + (current amount * elapsed seconds)
        return
            ObservationLib.Observation({
                amount: _currentTwab.amount +
                    _currentBalance *
                    (_time.checkedSub(_currentTwab.timestamp, _time)),
                timestamp: _time
            });
    }

    /// @notice Sets a new TWAB Observation at the next available index and returns the new account details.
    /// @dev Note that if _currentTime is before the last observation timestamp, it appears as an overflow
    /// @param _twabs The twabs array to insert into
    /// @param _accountDetails The current account details
    /// @param _currentTime The current time
    /// @return accountDetails The new account details
    /// @return twab The newest twab (may or may not be brand-new)
    /// @return isNew Whether the newest twab was created by this call
    function _nextTwab(
        ObservationLib.Observation[MAX_CARDINALITY] storage _twabs,
        AccountDetails memory _accountDetails,
        uint32 _currentTime
    )
        private
        returns (
            AccountDetails memory accountDetails,
            ObservationLib.Observation memory twab,
            bool isNew
        )
    {
        (, ObservationLib.Observation memory _newestTwab) = newestTwab(_twabs, _accountDetails);

        // if we're in the same block, return
        if (_newestTwab.timestamp == _currentTime) {
            return (_accountDetails, _newestTwab, false);
        }

        ObservationLib.Observation memory newTwab = _computeNextTwab(
            _newestTwab,
            _accountDetails.balance,
            _currentTime
        );

        _twabs[_accountDetails.nextTwabIndex] = newTwab;

        AccountDetails memory nextAccountDetails = push(_accountDetails);

        return (nextAccountDetails, newTwab, true);
    }

    /// @notice "Pushes" a new element on the AccountDetails ring buffer, and returns the new AccountDetails
    /// @param _accountDetails The account details from which to pull the cardinality and next index
    /// @return The new AccountDetails
    function push(AccountDetails memory _accountDetails)
        internal
        pure
        returns (AccountDetails memory)
    {
        _accountDetails.nextTwabIndex = uint24(
            RingBufferLib.nextIndex(_accountDetails.nextTwabIndex, MAX_CARDINALITY)
        );

        // Prevent the Account specific cardinality from exceeding the MAX_CARDINALITY.
        // The ring buffer length is limited by MAX_CARDINALITY. IF the account.cardinality
        // exceeds the max cardinality, new observations would be incorrectly set or the
        // observation would be out of "bounds" of the ring buffer. Once reached the
        // AccountDetails.cardinality will continue to be equal to max cardinality.
        if (_accountDetails.cardinality < MAX_CARDINALITY) {
            _accountDetails.cardinality += 1;
        }

        return _accountDetails;
    }
}

File 14 of 20 : IControlledToken.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.6;

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

/** @title IControlledToken
  * @author PoolTogether Inc Team
  * @notice ERC20 Tokens with a controller for minting & burning.
*/
interface IControlledToken is IERC20 {

    /** 
        @notice Interface to the contract responsible for controlling mint/burn
    */
    function controller() external view returns (address);

    /** 
      * @notice Allows the controller to mint tokens for a user account
      * @dev May be overridden to provide more granular control over minting
      * @param user Address of the receiver of the minted tokens
      * @param amount Amount of tokens to mint
    */
    function controllerMint(address user, uint256 amount) external;

    /** 
      * @notice Allows the controller to burn tokens from a user account
      * @dev May be overridden to provide more granular control over burning
      * @param user Address of the holder account to burn tokens from
      * @param amount Amount of tokens to burn
    */
    function controllerBurn(address user, uint256 amount) external;

    /** 
      * @notice Allows an operator via the controller to burn tokens on behalf of a user account
      * @dev May be overridden to provide more granular control over operator-burning
      * @param operator Address of the operator performing the burn action via the controller contract
      * @param user Address of the holder account to burn tokens from
      * @param amount Amount of tokens to burn
    */
    function controllerBurnFrom(
        address operator,
        address user,
        uint256 amount
    ) external;
}

File 15 of 20 : ExtendedSafeCastLib.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.6;

/**
 * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
 * checks.
 *
 * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
 * easily result in undesired exploitation or bugs, since developers usually
 * assume that overflows raise errors. `SafeCast` restores this intuition by
 * reverting the transaction when such an operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 *
 * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
 * all math on `uint256` and `int256` and then downcasting.
 */
library ExtendedSafeCastLib {

    /**
     * @dev Returns the downcasted uint104 from uint256, reverting on
     * overflow (when the input is greater than largest uint104).
     *
     * Counterpart to Solidity's `uint104` operator.
     *
     * Requirements:
     *
     * - input must fit into 104 bits
     */
    function toUint104(uint256 _value) internal pure returns (uint104) {
        require(_value <= type(uint104).max, "SafeCast: value doesn't fit in 104 bits");
        return uint104(_value);
    }

    /**
     * @dev Returns the downcasted uint208 from uint256, reverting on
     * overflow (when the input is greater than largest uint208).
     *
     * Counterpart to Solidity's `uint208` operator.
     *
     * Requirements:
     *
     * - input must fit into 208 bits
     */
    function toUint208(uint256 _value) internal pure returns (uint208) {
        require(_value <= type(uint208).max, "SafeCast: value doesn't fit in 208 bits");
        return uint208(_value);
    }

    /**
     * @dev Returns the downcasted uint224 from uint256, reverting on
     * overflow (when the input is greater than largest uint224).
     *
     * Counterpart to Solidity's `uint224` operator.
     *
     * Requirements:
     *
     * - input must fit into 224 bits
     */
    function toUint224(uint256 _value) internal pure returns (uint224) {
        require(_value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits");
        return uint224(_value);
    }
}

File 16 of 20 : OverflowSafeComparatorLib.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.6;

/// @title OverflowSafeComparatorLib library to share comparator functions between contracts
/// @dev Code taken from Uniswap V3 Oracle.sol: https://github.com/Uniswap/v3-core/blob/3e88af408132fc957e3e406f65a0ce2b1ca06c3d/contracts/libraries/Oracle.sol
/// @author PoolTogether Inc.
library OverflowSafeComparatorLib {
    /// @notice 32-bit timestamps comparator.
    /// @dev safe for 0 or 1 overflows, `_a` and `_b` must be chronologically before or equal to time.
    /// @param _a A comparison timestamp from which to determine the relative position of `_timestamp`.
    /// @param _b Timestamp to compare against `_a`.
    /// @param _timestamp A timestamp truncated to 32 bits.
    /// @return bool Whether `_a` is chronologically < `_b`.
    function lt(
        uint32 _a,
        uint32 _b,
        uint32 _timestamp
    ) internal pure returns (bool) {
        // No need to adjust if there hasn't been an overflow
        if (_a <= _timestamp && _b <= _timestamp) return _a < _b;

        uint256 aAdjusted = _a > _timestamp ? _a : _a + 2**32;
        uint256 bAdjusted = _b > _timestamp ? _b : _b + 2**32;

        return aAdjusted < bAdjusted;
    }

    /// @notice 32-bit timestamps comparator.
    /// @dev safe for 0 or 1 overflows, `_a` and `_b` must be chronologically before or equal to time.
    /// @param _a A comparison timestamp from which to determine the relative position of `_timestamp`.
    /// @param _b Timestamp to compare against `_a`.
    /// @param _timestamp A timestamp truncated to 32 bits.
    /// @return bool Whether `_a` is chronologically <= `_b`.
    function lte(
        uint32 _a,
        uint32 _b,
        uint32 _timestamp
    ) internal pure returns (bool) {

        // No need to adjust if there hasn't been an overflow
        if (_a <= _timestamp && _b <= _timestamp) return _a <= _b;

        uint256 aAdjusted = _a > _timestamp ? _a : _a + 2**32;
        uint256 bAdjusted = _b > _timestamp ? _b : _b + 2**32;

        return aAdjusted <= bAdjusted;
    }

    /// @notice 32-bit timestamp subtractor
    /// @dev safe for 0 or 1 overflows, where `_a` and `_b` must be chronologically before or equal to time
    /// @param _a The subtraction left operand
    /// @param _b The subtraction right operand
    /// @param _timestamp The current time.  Expected to be chronologically after both.
    /// @return The difference between a and b, adjusted for overflow
    function checkedSub(
        uint32 _a,
        uint32 _b,
        uint32 _timestamp
    ) internal pure returns (uint32) {
        // No need to adjust if there hasn't been an overflow

        if (_a <= _timestamp && _b <= _timestamp) return _a - _b;

        uint256 aAdjusted = _a > _timestamp ? _a : _a + 2**32;
        uint256 bAdjusted = _b > _timestamp ? _b : _b + 2**32;

        return uint32(aAdjusted - bAdjusted);
    }
}

File 17 of 20 : RingBufferLib.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.6;

library RingBufferLib {
    /**
    * @notice Returns wrapped TWAB index.
    * @dev  In order to navigate the TWAB circular buffer, we need to use the modulo operator.
    * @dev  For example, if `_index` is equal to 32 and the TWAB circular buffer is of `_cardinality` 32,
    *       it will return 0 and will point to the first element of the array.
    * @param _index Index used to navigate through the TWAB circular buffer.
    * @param _cardinality TWAB buffer cardinality.
    * @return TWAB index.
    */
    function wrap(uint256 _index, uint256 _cardinality) internal pure returns (uint256) {
        return _index % _cardinality;
    }

    /**
    * @notice Computes the negative offset from the given index, wrapped by the cardinality.
    * @dev  We add `_cardinality` to `_index` to be able to offset even if `_amount` is superior to `_cardinality`.
    * @param _index The index from which to offset
    * @param _amount The number of indices to offset.  This is subtracted from the given index.
    * @param _cardinality The number of elements in the ring buffer
    * @return Offsetted index.
     */
    function offset(
        uint256 _index,
        uint256 _amount,
        uint256 _cardinality
    ) internal pure returns (uint256) {
        return wrap(_index + _cardinality - _amount, _cardinality);
    }

    /// @notice Returns the index of the last recorded TWAB
    /// @param _nextIndex The next available twab index.  This will be recorded to next.
    /// @param _cardinality The cardinality of the TWAB history.
    /// @return The index of the last recorded TWAB
    function newestIndex(uint256 _nextIndex, uint256 _cardinality)
        internal
        pure
        returns (uint256)
    {
        if (_cardinality == 0) {
            return 0;
        }

        return wrap(_nextIndex + _cardinality - 1, _cardinality);
    }

    /// @notice Computes the ring buffer index that follows the given one, wrapped by cardinality
    /// @param _index The index to increment
    /// @param _cardinality The number of elements in the Ring Buffer
    /// @return The next index relative to the given index.  Will wrap around to 0 if the next index == cardinality
    function nextIndex(uint256 _index, uint256 _cardinality)
        internal
        pure
        returns (uint256)
    {
        return wrap(_index + 1, _cardinality);
    }
}

File 18 of 20 : ObservationLib.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.6;

import "@openzeppelin/contracts/utils/math/SafeCast.sol";

import "./OverflowSafeComparatorLib.sol";
import "./RingBufferLib.sol";

/**
* @title Observation Library
* @notice This library allows one to store an array of timestamped values and efficiently binary search them.
* @dev Largely pulled from Uniswap V3 Oracle.sol: https://github.com/Uniswap/v3-core/blob/c05a0e2c8c08c460fb4d05cfdda30b3ad8deeaac/contracts/libraries/Oracle.sol
* @author PoolTogether Inc.
*/
library ObservationLib {
    using OverflowSafeComparatorLib for uint32;
    using SafeCast for uint256;

    /// @notice The maximum number of observations
    uint24 public constant MAX_CARDINALITY = 16777215; // 2**24

    /**
    * @notice Observation, which includes an amount and timestamp.
    * @param amount `amount` at `timestamp`.
    * @param timestamp Recorded `timestamp`.
    */
    struct Observation {
        uint224 amount;
        uint32 timestamp;
    }

    /**
    * @notice Fetches Observations `beforeOrAt` and `atOrAfter` a `_target`, eg: where [`beforeOrAt`, `atOrAfter`] is satisfied.
    * The result may be the same Observation, or adjacent Observations.
    * @dev The answer must be contained in the array used when the target is located within the stored Observation.
    * boundaries: older than the most recent Observation and younger, or the same age as, the oldest Observation.
    * @dev  If `_newestObservationIndex` is less than `_oldestObservationIndex`, it means that we've wrapped around the circular buffer.
    *       So the most recent observation will be at `_oldestObservationIndex + _cardinality - 1`, at the beginning of the circular buffer.
    * @param _observations List of Observations to search through.
    * @param _newestObservationIndex Index of the newest Observation. Right side of the circular buffer.
    * @param _oldestObservationIndex Index of the oldest Observation. Left side of the circular buffer.
    * @param _target Timestamp at which we are searching the Observation.
    * @param _cardinality Cardinality of the circular buffer we are searching through.
    * @param _time Timestamp at which we perform the binary search.
    * @return beforeOrAt Observation recorded before, or at, the target.
    * @return atOrAfter Observation recorded at, or after, the target.
    */
    function binarySearch(
        Observation[MAX_CARDINALITY] storage _observations,
        uint24 _newestObservationIndex,
        uint24 _oldestObservationIndex,
        uint32 _target,
        uint24 _cardinality,
        uint32 _time
    ) internal view returns (Observation memory beforeOrAt, Observation memory atOrAfter) {
        uint256 leftSide = _oldestObservationIndex;
        uint256 rightSide = _newestObservationIndex < leftSide
            ? leftSide + _cardinality - 1
            : _newestObservationIndex;
        uint256 currentIndex;

        while (true) {
            // We start our search in the middle of the `leftSide` and `rightSide`.
            // After each iteration, we narrow down the search to the left or the right side while still starting our search in the middle.
            currentIndex = (leftSide + rightSide) / 2;

            beforeOrAt = _observations[uint24(RingBufferLib.wrap(currentIndex, _cardinality))];
            uint32 beforeOrAtTimestamp = beforeOrAt.timestamp;

            // We've landed on an uninitialized timestamp, keep searching higher (more recently).
            if (beforeOrAtTimestamp == 0) {
                leftSide = currentIndex + 1;
                continue;
            }

            atOrAfter = _observations[uint24(RingBufferLib.nextIndex(currentIndex, _cardinality))];

            bool targetAtOrAfter = beforeOrAtTimestamp.lte(_target, _time);

            // Check if we've found the corresponding Observation.
            if (targetAtOrAfter && _target.lte(atOrAfter.timestamp, _time)) {
                break;
            }

            // If `beforeOrAtTimestamp` is greater than `_target`, then we keep searching lower. To the left of the current index.
            if (!targetAtOrAfter) {
                rightSide = currentIndex - 1;
            } else {
                // Otherwise, we keep searching higher. To the left of the current index.
                leftSide = currentIndex + 1;
            }
        }
    }
}

File 19 of 20 : SafeCast.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/math/SafeCast.sol)

pragma solidity ^0.8.0;

/**
 * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
 * checks.
 *
 * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
 * easily result in undesired exploitation or bugs, since developers usually
 * assume that overflows raise errors. `SafeCast` restores this intuition by
 * reverting the transaction when such an operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 *
 * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
 * all math on `uint256` and `int256` and then downcasting.
 */
library SafeCast {
    /**
     * @dev Returns the downcasted uint224 from uint256, reverting on
     * overflow (when the input is greater than largest uint224).
     *
     * Counterpart to Solidity's `uint224` operator.
     *
     * Requirements:
     *
     * - input must fit into 224 bits
     */
    function toUint224(uint256 value) internal pure returns (uint224) {
        require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits");
        return uint224(value);
    }

    /**
     * @dev Returns the downcasted uint128 from uint256, reverting on
     * overflow (when the input is greater than largest uint128).
     *
     * Counterpart to Solidity's `uint128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     */
    function toUint128(uint256 value) internal pure returns (uint128) {
        require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits");
        return uint128(value);
    }

    /**
     * @dev Returns the downcasted uint96 from uint256, reverting on
     * overflow (when the input is greater than largest uint96).
     *
     * Counterpart to Solidity's `uint96` operator.
     *
     * Requirements:
     *
     * - input must fit into 96 bits
     */
    function toUint96(uint256 value) internal pure returns (uint96) {
        require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits");
        return uint96(value);
    }

    /**
     * @dev Returns the downcasted uint64 from uint256, reverting on
     * overflow (when the input is greater than largest uint64).
     *
     * Counterpart to Solidity's `uint64` operator.
     *
     * Requirements:
     *
     * - input must fit into 64 bits
     */
    function toUint64(uint256 value) internal pure returns (uint64) {
        require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits");
        return uint64(value);
    }

    /**
     * @dev Returns the downcasted uint32 from uint256, reverting on
     * overflow (when the input is greater than largest uint32).
     *
     * Counterpart to Solidity's `uint32` operator.
     *
     * Requirements:
     *
     * - input must fit into 32 bits
     */
    function toUint32(uint256 value) internal pure returns (uint32) {
        require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits");
        return uint32(value);
    }

    /**
     * @dev Returns the downcasted uint16 from uint256, reverting on
     * overflow (when the input is greater than largest uint16).
     *
     * Counterpart to Solidity's `uint16` operator.
     *
     * Requirements:
     *
     * - input must fit into 16 bits
     */
    function toUint16(uint256 value) internal pure returns (uint16) {
        require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits");
        return uint16(value);
    }

    /**
     * @dev Returns the downcasted uint8 from uint256, reverting on
     * overflow (when the input is greater than largest uint8).
     *
     * Counterpart to Solidity's `uint8` operator.
     *
     * Requirements:
     *
     * - input must fit into 8 bits.
     */
    function toUint8(uint256 value) internal pure returns (uint8) {
        require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits");
        return uint8(value);
    }

    /**
     * @dev Converts a signed int256 into an unsigned uint256.
     *
     * Requirements:
     *
     * - input must be greater than or equal to 0.
     */
    function toUint256(int256 value) internal pure returns (uint256) {
        require(value >= 0, "SafeCast: value must be positive");
        return uint256(value);
    }

    /**
     * @dev Returns the downcasted int128 from int256, reverting on
     * overflow (when the input is less than smallest int128 or
     * greater than largest int128).
     *
     * Counterpart to Solidity's `int128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     *
     * _Available since v3.1._
     */
    function toInt128(int256 value) internal pure returns (int128) {
        require(value >= type(int128).min && value <= type(int128).max, "SafeCast: value doesn't fit in 128 bits");
        return int128(value);
    }

    /**
     * @dev Returns the downcasted int64 from int256, reverting on
     * overflow (when the input is less than smallest int64 or
     * greater than largest int64).
     *
     * Counterpart to Solidity's `int64` operator.
     *
     * Requirements:
     *
     * - input must fit into 64 bits
     *
     * _Available since v3.1._
     */
    function toInt64(int256 value) internal pure returns (int64) {
        require(value >= type(int64).min && value <= type(int64).max, "SafeCast: value doesn't fit in 64 bits");
        return int64(value);
    }

    /**
     * @dev Returns the downcasted int32 from int256, reverting on
     * overflow (when the input is less than smallest int32 or
     * greater than largest int32).
     *
     * Counterpart to Solidity's `int32` operator.
     *
     * Requirements:
     *
     * - input must fit into 32 bits
     *
     * _Available since v3.1._
     */
    function toInt32(int256 value) internal pure returns (int32) {
        require(value >= type(int32).min && value <= type(int32).max, "SafeCast: value doesn't fit in 32 bits");
        return int32(value);
    }

    /**
     * @dev Returns the downcasted int16 from int256, reverting on
     * overflow (when the input is less than smallest int16 or
     * greater than largest int16).
     *
     * Counterpart to Solidity's `int16` operator.
     *
     * Requirements:
     *
     * - input must fit into 16 bits
     *
     * _Available since v3.1._
     */
    function toInt16(int256 value) internal pure returns (int16) {
        require(value >= type(int16).min && value <= type(int16).max, "SafeCast: value doesn't fit in 16 bits");
        return int16(value);
    }

    /**
     * @dev Returns the downcasted int8 from int256, reverting on
     * overflow (when the input is less than smallest int8 or
     * greater than largest int8).
     *
     * Counterpart to Solidity's `int8` operator.
     *
     * Requirements:
     *
     * - input must fit into 8 bits.
     *
     * _Available since v3.1._
     */
    function toInt8(int256 value) internal pure returns (int8) {
        require(value >= type(int8).min && value <= type(int8).max, "SafeCast: value doesn't fit in 8 bits");
        return int8(value);
    }

    /**
     * @dev Converts an unsigned uint256 into a signed int256.
     *
     * Requirements:
     *
     * - input must be less than or equal to maxInt256.
     */
    function toInt256(uint256 value) internal pure returns (int256) {
        // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
        require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256");
        return int256(value);
    }
}

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

pragma solidity ^0.8.0;

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

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

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

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

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"string","name":"name_","type":"string"},{"internalType":"string","name":"symbol_","type":"string"},{"internalType":"contract ITicket","name":"_ticket","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegator","type":"address"},{"indexed":true,"internalType":"uint256","name":"slot","type":"uint256"},{"indexed":true,"internalType":"address","name":"delegatee","type":"address"},{"indexed":false,"internalType":"uint96","name":"lockUntil","type":"uint96"},{"indexed":false,"internalType":"address","name":"user","type":"address"}],"name":"DelegateeUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegator","type":"address"},{"indexed":true,"internalType":"uint256","name":"slot","type":"uint256"},{"indexed":false,"internalType":"uint96","name":"lockUntil","type":"uint96"},{"indexed":true,"internalType":"address","name":"delegatee","type":"address"},{"indexed":false,"internalType":"contract Delegation","name":"delegation","type":"address"},{"indexed":false,"internalType":"address","name":"user","type":"address"}],"name":"DelegationCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegator","type":"address"},{"indexed":true,"internalType":"uint256","name":"slot","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":true,"internalType":"address","name":"user","type":"address"}],"name":"DelegationFunded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegator","type":"address"},{"indexed":true,"internalType":"uint256","name":"slot","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":true,"internalType":"address","name":"user","type":"address"}],"name":"DelegationFundedFromStake","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegator","type":"address"},{"indexed":true,"internalType":"address","name":"representative","type":"address"},{"indexed":false,"internalType":"bool","name":"set","type":"bool"}],"name":"RepresentativeSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract ITicket","name":"ticket","type":"address"}],"name":"TicketSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegator","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TicketsStaked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegator","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TicketsUnstaked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegator","type":"address"},{"indexed":true,"internalType":"uint256","name":"slot","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"TransferredDelegation","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegator","type":"address"},{"indexed":true,"internalType":"uint256","name":"slot","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":true,"internalType":"address","name":"user","type":"address"}],"name":"WithdrewDelegationToStake","type":"event"},{"inputs":[],"name":"MAX_LOCK","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_delegator","type":"address"},{"internalType":"uint256","name":"_slot","type":"uint256"}],"name":"computeDelegationAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_delegator","type":"address"},{"internalType":"uint256","name":"_slot","type":"uint256"},{"internalType":"address","name":"_delegatee","type":"address"},{"internalType":"uint96","name":"_lockDuration","type":"uint96"}],"name":"createDelegation","outputs":[{"internalType":"contract Delegation","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"delegationInstance","outputs":[{"internalType":"contract Delegation","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_delegator","type":"address"},{"internalType":"uint256","name":"_slot","type":"uint256"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"fundDelegation","outputs":[{"internalType":"contract Delegation","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_delegator","type":"address"},{"internalType":"uint256","name":"_slot","type":"uint256"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"fundDelegationFromStake","outputs":[{"internalType":"contract Delegation","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_delegator","type":"address"},{"internalType":"uint256","name":"_slot","type":"uint256"}],"name":"getDelegation","outputs":[{"internalType":"contract Delegation","name":"delegation","type":"address"},{"internalType":"address","name":"delegatee","type":"address"},{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"uint256","name":"lockUntil","type":"uint256"},{"internalType":"bool","name":"wasCreated","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_delegator","type":"address"},{"internalType":"address","name":"_representative","type":"address"}],"name":"isRepresentativeOf","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes[]","name":"_data","type":"bytes[]"}],"name":"multicall","outputs":[{"internalType":"bytes[]","name":"","type":"bytes[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"},{"components":[{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"internalType":"struct PermitAndMulticall.Signature","name":"_permitSignature","type":"tuple"},{"internalType":"bytes[]","name":"_data","type":"bytes[]"}],"name":"permitAndMulticall","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_representative","type":"address"},{"internalType":"bool","name":"_set","type":"bool"}],"name":"setRepresentative","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"stake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ticket","outputs":[{"internalType":"contract ITicket","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_slot","type":"uint256"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"address","name":"_to","type":"address"}],"name":"transferDelegationTo","outputs":[{"internalType":"contract Delegation","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"unstake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_delegator","type":"address"},{"internalType":"uint256","name":"_slot","type":"uint256"},{"internalType":"address","name":"_delegatee","type":"address"},{"internalType":"uint96","name":"_lockDuration","type":"uint96"}],"name":"updateDelegatee","outputs":[{"internalType":"contract Delegation","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_delegator","type":"address"},{"internalType":"uint256","name":"_slot","type":"uint256"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"withdrawDelegationToStake","outputs":[{"internalType":"contract Delegation","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"}]

60a06040523480156200001157600080fd5b50604051620038a2380380620038a2833981016040819052620000349162000317565b8251839083906200004d906003906020850190620001ac565b50805162000063906004906020840190620001ac565b50505060405162000074906200023b565b604051809103906000f08015801562000091573d6000803e3d6000fd5b50600580546001600160a01b0319166001600160a01b0392909216918217905560405163909f1cad60e01b81526000600482015263909f1cad90602401600060405180830381600087803b158015620000e957600080fd5b505af1158015620000fe573d6000803e3d6000fd5b505050506001600160a01b0381166200015d5760405162461bcd60e51b815260206004820181905260248201527f5457414244656c656761746f722f7469636b2d6e6f742d7a65726f2d61646472604482015260640160405180910390fd5b6001600160601b0319606082901b166080526040516001600160a01b038216907f9f9d59c87dbdc6ca82d9e5924782004b9aebc366c505c0ccab12f61e2a9f332190600090a2505050620003f7565b828054620001ba90620003a4565b90600052602060002090601f016020900481019282620001de576000855562000229565b82601f10620001f957805160ff191683800117855562000229565b8280016001018555821562000229579182015b82811115620002295782518255916020019190600101906200020c565b506200023792915062000249565b5090565b6107fc80620030a683390190565b5b808211156200023757600081556001016200024a565b600082601f8301126200027257600080fd5b81516001600160401b03808211156200028f576200028f620003e1565b604051601f8301601f19908116603f01168101908282118183101715620002ba57620002ba620003e1565b81604052838152602092508683858801011115620002d757600080fd5b600091505b83821015620002fb5785820183015181830184015290820190620002dc565b838211156200030d5760008385830101525b9695505050505050565b6000806000606084860312156200032d57600080fd5b83516001600160401b03808211156200034557600080fd5b620003538783880162000260565b945060208601519150808211156200036a57600080fd5b50620003798682870162000260565b604086015190935090506001600160a01b03811681146200039957600080fd5b809150509250925092565b600181811c90821680620003b957607f821691505b60208210811415620003db57634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052604160045260246000fd5b60805160601c612c5162000455600039600081816102e4015281816104c4015281816106650152818161083501528181610d1e01528181610dc001528181610e8001528181610f3701528181611080015261201e0152612c516000f3fe608060405234801561001057600080fd5b50600436106101c45760003560e01c8063889de805116100f9578063ac9650d811610097578063ca40edf111610071578063ca40edf114610418578063dd62ed3e14610460578063e18fa6eb14610499578063e7880ae1146104ac57600080fd5b8063ac9650d8146103d2578063adc9772e146103f2578063c2a672e01461040557600080fd5b806395d89b41116100d357806395d89b4114610391578063982b1f2f14610399578063a457c2d7146103ac578063a9059cbb146103bf57600080fd5b8063889de8051461032f5780638b4b4ec91461034257806390ab08851461035557600080fd5b80635f66501111610166578063666f7af611610140578063666f7af6146102b95780636c59f295146102cc5780636cc25db7146102df57806370a082311461030657600080fd5b80635f6650111461027157806363fc611f1461029c57806365a5d5f0146102af57600080fd5b806318160ddd116101a257806318160ddd1461021f57806323b872dd14610231578063313ce56714610244578063395093511461025e57600080fd5b806306452792146101c957806306fdde03146101de578063095ea7b3146101fc575b600080fd5b6101dc6101d73660046127d1565b6104bf565b005b6101e66104f2565b6040516101f391906129ee565b60405180910390f35b61020f61020a36600461258c565b610584565b60405190151581526020016101f3565b6002545b6040519081526020016101f3565b61020f61023f36600461251d565b61059b565b61024c610661565b60405160ff90911681526020016101f3565b61020f61026c36600461258c565b6106f9565b61028461027f36600461260b565b610735565b6040516001600160a01b0390911681526020016101f3565b600554610284906001600160a01b031681565b61022362ed4e0081565b6102846102c736600461260b565b6107b9565b6102846102da3660046125b8565b6108a3565b6102847f000000000000000000000000000000000000000000000000000000000000000081565b6102236103143660046124aa565b6001600160a01b031660009081526020819052604090205490565b61028461033d3660046125b8565b6109e9565b610284610350366004612837565b610ae4565b61020f6103633660046124e4565b6001600160a01b03918216600090815260066020908152604080832093909416825291909152205460ff1690565b6101e6610b4e565b6101dc6103a736600461255e565b610b5d565b61020f6103ba36600461258c565b610c3e565b61020f6103cd36600461258c565b610cef565b6103e56103e0366004612640565b610cfc565b6040516101f3919061290f565b6101dc61040036600461258c565b610d08565b6101dc61041336600461258c565b610d97565b61042b61042636600461258c565b610e24565b604080516001600160a01b0396871681529590941660208601529284019190915260608301521515608082015260a0016101f3565b61022361046e3660046124e4565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b6102846104a736600461260b565b611047565b6102846104ba36600461258c565b6110ed565b6104ec7f0000000000000000000000000000000000000000000000000000000000000000858585856110f9565b50505050565b60606003805461050190612af2565b80601f016020809104026020016040519081016040528092919081815260200182805461052d90612af2565b801561057a5780601f1061054f5761010080835404028352916020019161057a565b820191906000526020600020905b81548152906001019060200180831161055d57829003601f168201915b5050505050905090565b60006105913384846111d1565b5060015b92915050565b60006105a8848484611329565b6001600160a01b0384166000908152600160209081526040808320338452909152902054828110156106475760405162461bcd60e51b815260206004820152602860248201527f45524332303a207472616e7366657220616d6f756e742065786365656473206160448201527f6c6c6f77616e636500000000000000000000000000000000000000000000000060648201526084015b60405180910390fd5b61065485338584036111d1565b60019150505b9392505050565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b1580156106bc57600080fd5b505afa1580156106d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106f4919061288d565b905090565b3360008181526001602090815260408083206001600160a01b03871684529091528120549091610591918590610730908690612a97565b6111d1565b600061074084611540565b600061074c85856115c9565b905061075981308561161f565b6107638584611641565b336001600160a01b031684866001600160a01b03167f6862a473baa6176f1c866c69aa93da8508d7afc71b52dddc9d5e8b0bb7aab6f4866040516107a991815260200190565b60405180910390a4949350505050565b60006001600160a01b0384166108115760405162461bcd60e51b815260206004820181905260248201527f5457414244656c656761746f722f646c6774722d6e6f742d7a65726f2d616472604482015260640161063e565b61081a82611719565b600061082685856115c9565b905061085d6001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016338386611769565b336001600160a01b031684866001600160a01b03167f383183291bd9a7fb8bd9c7c86c5013a89d1490c9f4e486da279804b83729a1dc866040516107a991815260200190565b60006108ae85611540565b6108b78361181a565b6108ce826bffffffffffffffffffffffff16611870565b60006108da86866115c9565b90506108e5816118c3565b4283016bffffffffffffffffffffffff84161561097d576040517fac2293af0000000000000000000000000000000000000000000000000000000081526bffffffffffffffffffffffff821660048201526001600160a01b0383169063ac2293af90602401600060405180830381600087803b15801561096457600080fd5b505af1158015610978573d6000803e3d6000fd5b505050505b6109878286611991565b604080516bffffffffffffffffffffffff831681523360208201526001600160a01b03808816928992918b16917ffd96a87f22afea1e17a7117a4923f1499a1c1eb2bd7c492caf07f3a3c38ade6f910160405180910390a45095945050505050565b60006109f485611540565b6109fd8361181a565b610a14826bffffffffffffffffffffffff16611870565b4282016000610a6e610a6888886040516bffffffffffffffffffffffff19606084901b1660208201526034810182905260009060540160405160208183030381529060405280519060200120905092915050565b83611a17565b9050610a7a8186611991565b604080516bffffffffffffffffffffffff841681526001600160a01b03838116602083015233828401529151878316928992908b16917f5533acb96061e404278604d3df68397263be1d4b9df394136a2968802633d8a59181900360600190a49695505050505050565b6000610aef82611abd565b6000610afb33866115c9565b9050610b0881848661161f565b826001600160a01b031685336001600160a01b03167f622b7da8a20026f1176ccc7ec0a635a4544a67e99b0125018e3d89b888ce8ebe876040516107a991815260200190565b60606004805461050190612af2565b6001600160a01b038216610bb35760405162461bcd60e51b815260206004820152601f60248201527f5457414244656c656761746f722f7265702d6e6f742d7a65726f2d6164647200604482015260640161063e565b3360008181526006602090815260408083206001600160a01b0387168085529083529281902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001686151590811790915590519081529192917f50062a33e55b9f3dfcf05fbf1356b7c92313796cfb8526cdee5a497fcbb8cc3391015b60405180910390a35050565b3360009081526001602090815260408083206001600160a01b038616845290915281205482811015610cd85760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f7760448201527f207a65726f000000000000000000000000000000000000000000000000000000606482015260840161063e565b610ce533858584036111d1565b5060019392505050565b6000610591338484611329565b606061065a8383611b13565b610d1181611719565b610d466001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016333084611769565b610d508282611641565b816001600160a01b03167f15c2c6e5db9e25d828754c9c5cee6c5c3df074e6ac26e7491c0f8ce7bbd447d682604051610d8b91815260200190565b60405180910390a25050565b610da082611abd565b610da981611719565b610db33382611c0d565b610de76001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168383611d92565b6040518181526001600160a01b0383169033907ff7128607975b3ff61bc02d19a1c8267e526f7a5b7144dc4efcdac634663fd36990602001610c32565b6000806000806000610e3687876115c9565b94506001600160a01b0385163b15156040517f8d22ea2a0000000000000000000000000000000000000000000000000000000081526001600160a01b0387811660048301529192507f000000000000000000000000000000000000000000000000000000000000000090911690638d22ea2a9060240160206040518083038186803b158015610ec457600080fd5b505afa158015610ed8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610efc91906124c7565b6040517f70a082310000000000000000000000000000000000000000000000000000000081526001600160a01b0387811660048301529195507f0000000000000000000000000000000000000000000000000000000000000000909116906370a082319060240160206040518083038186803b158015610f7b57600080fd5b505afa158015610f8f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fb391906127b8565b9250801561103d57846001600160a01b0316633c78929e6040518163ffffffff1660e01b815260040160206040518083038186803b158015610ff457600080fd5b505afa158015611008573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061102c91906128aa565b6bffffffffffffffffffffffff1691505b9295509295909350565b600061105284611540565b61105b82611719565b600061106785856115c9565b90506110738584611c0d565b6110a76001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168285611d92565b336001600160a01b031684866001600160a01b03167fb1968721eeb35d2206c8aa91805bc908019965ff4cff13c158f89956fb8e9248866040516107a991815260200190565b600061065a83836115c9565b6001600160a01b03851663d505accf333087873561111d60408a0160208b01612870565b604080517fffffffff0000000000000000000000000000000000000000000000000000000060e089901b1681526001600160a01b0396871660048201529590941660248601526044850192909252606484015260ff16608483015286013560a4820152606086013560c482015260e401600060405180830381600087803b1580156111a757600080fd5b505af11580156111bb573d6000803e3d6000fd5b505050506111c98282611b13565b505050505050565b6001600160a01b03831661124c5760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f2061646460448201527f7265737300000000000000000000000000000000000000000000000000000000606482015260840161063e565b6001600160a01b0382166112c85760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f20616464726560448201527f7373000000000000000000000000000000000000000000000000000000000000606482015260840161063e565b6001600160a01b0383811660008181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050565b6001600160a01b0383166113a55760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f20616460448201527f6472657373000000000000000000000000000000000000000000000000000000606482015260840161063e565b6001600160a01b0382166114215760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201527f6573730000000000000000000000000000000000000000000000000000000000606482015260840161063e565b6001600160a01b038316600090815260208190526040902054818110156114b05760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e742065786365656473206260448201527f616c616e63650000000000000000000000000000000000000000000000000000606482015260840161063e565b6001600160a01b038085166000908152602081905260408082208585039055918516815290812080548492906114e7908490612a97565b92505081905550826001600160a01b0316846001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8460405161153391815260200190565b60405180910390a36104ec565b6001600160a01b03811633148061157a57506001600160a01b038116600090815260066020908152604080832033845290915290205460ff165b6115c65760405162461bcd60e51b815260206004820152601e60248201527f5457414244656c656761746f722f6e6f742d646c6774722d6f722d7265700000604482015260640161063e565b50565b600061065a61161a84846040516bffffffffffffffffffffffff19606084901b1660208201526034810182905260009060540160405160208183030381529060405280519060200120905092915050565b611ddb565b61162881611719565b611631836118c3565b61163c838383611e64565b505050565b6001600160a01b0382166116975760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f206164647265737300604482015260640161063e565b80600260008282546116a99190612a97565b90915550506001600160a01b038216600090815260208190526040812080548392906116d6908490612a97565b90915550506040518181526001600160a01b038316906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602001610c32565b600081116115c65760405162461bcd60e51b815260206004820152601c60248201527f5457414244656c656761746f722f616d6f756e742d67742d7a65726f00000000604482015260640161063e565b6040516001600160a01b03808516602483015283166044820152606481018290526104ec9085907f23b872dd00000000000000000000000000000000000000000000000000000000906084015b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090931692909217909152611ee7565b6001600160a01b0381166115c65760405162461bcd60e51b815260206004820181905260248201527f5457414244656c656761746f722f646c67742d6e6f742d7a65726f2d61646472604482015260640161063e565b62ed4e008111156115c65760405162461bcd60e51b815260206004820152601b60248201527f5457414244656c656761746f722f6c6f636b2d746f6f2d6c6f6e670000000000604482015260640161063e565b806001600160a01b0316633c78929e6040518163ffffffff1660e01b815260040160206040518083038186803b1580156118fc57600080fd5b505afa158015611910573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061193491906128aa565b6bffffffffffffffffffffffff164210156115c65760405162461bcd60e51b815260206004820152601f60248201527f5457414244656c656761746f722f64656c65676174696f6e2d6c6f636b656400604482015260640161063e565b604080516001600160a01b0383166024808301919091528251808303909101815260449091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f5c19a95c0000000000000000000000000000000000000000000000000000000090811790915290611a108482611fcc565b5050505050565b6005546000908190611a32906001600160a01b031685612110565b6040517f909f1cad0000000000000000000000000000000000000000000000000000000081526bffffffffffffffffffffffff851660048201529091506001600160a01b0382169063909f1cad90602401600060405180830381600087803b158015611a9d57600080fd5b505af1158015611ab1573d6000803e3d6000fd5b50929695505050505050565b6001600160a01b0381166115c65760405162461bcd60e51b815260206004820152601e60248201527f5457414244656c656761746f722f746f2d6e6f742d7a65726f2d616464720000604482015260640161063e565b60608160008167ffffffffffffffff811115611b3157611b31612b92565b604051908082528060200260200182016040528015611b6457816020015b6060815260200190600190039081611b4f5790505b50905060005b82811015611c0457611bd430878784818110611b8857611b88612b7c565b9050602002810190611b9a9190612a01565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506121c792505050565b828281518110611be657611be6612b7c565b60200260200101819052508080611bfc90612b2d565b915050611b6a565b50949350505050565b6001600160a01b038216611c895760405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f2061646472657360448201527f7300000000000000000000000000000000000000000000000000000000000000606482015260840161063e565b6001600160a01b03821660009081526020819052604090205481811015611d185760405162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e60448201527f6365000000000000000000000000000000000000000000000000000000000000606482015260840161063e565b6001600160a01b0383166000908152602081905260408120838303905560028054849290611d47908490612aaf565b90915550506040518281526000906001600160a01b038516907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a3505050565b6040516001600160a01b03831660248201526044810182905261163c9084907fa9059cbb00000000000000000000000000000000000000000000000000000000906064016117b6565b600554600090610595906001600160a01b031683306040517f3d602d80600a3d3981f3363d3d373d3d3d363d730000000000000000000000008152606093841b60148201527f5af43d82803e903d91602b57fd5bf3ff000000000000000000000000000000006028820152921b6038830152604c8201526037808220606c830152605591012090565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb00000000000000000000000000000000000000000000000000000000908117909152906111c98582611fcc565b6000611f3c826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166121ec9092919063ffffffff16565b80519091501561163c5780806020019051810190611f5a919061279b565b61163c5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f74207375636365656400000000000000000000000000000000000000000000606482015260840161063e565b60408051600180825281830190925260609160009190816020015b604080518082019091526000815260606020820152815260200190600190039081611fe757905050905060405180604001604052807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602001848152508160008151811061206257612062612b7c565b60209081029190910101526040517fde9443bf0000000000000000000000000000000000000000000000000000000081526001600160a01b0385169063de9443bf906120b2908490600401612971565b600060405180830381600087803b1580156120cc57600080fd5b505af11580156120e0573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526121089190810190612682565b949350505050565b60006040517f3d602d80600a3d3981f3363d3d373d3d3d363d7300000000000000000000000081528360601b60148201527f5af43d82803e903d91602b57fd5bf300000000000000000000000000000000006028820152826037826000f59150506001600160a01b0381166105955760405162461bcd60e51b815260206004820152601760248201527f455243313136373a2063726561746532206661696c6564000000000000000000604482015260640161063e565b606061065a8383604051806060016040528060278152602001612bf5602791396121fb565b606061210884846000856122e6565b6060833b6122715760405162461bcd60e51b815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f60448201527f6e74726163740000000000000000000000000000000000000000000000000000606482015260840161063e565b600080856001600160a01b03168560405161228c91906128f3565b600060405180830381855af49150503d80600081146122c7576040519150601f19603f3d011682016040523d82523d6000602084013e6122cc565b606091505b50915091506122dc828286612425565b9695505050505050565b60608247101561235e5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c0000000000000000000000000000000000000000000000000000606482015260840161063e565b843b6123ac5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640161063e565b600080866001600160a01b031685876040516123c891906128f3565b60006040518083038185875af1925050503d8060008114612405576040519150601f19603f3d011682016040523d82523d6000602084013e61240a565b606091505b509150915061241a828286612425565b979650505050505050565b6060831561243457508161065a565b8251156124445782518084602001fd5b8160405162461bcd60e51b815260040161063e91906129ee565b60008083601f84011261247057600080fd5b50813567ffffffffffffffff81111561248857600080fd5b6020830191508360208260051b85010111156124a357600080fd5b9250929050565b6000602082840312156124bc57600080fd5b813561065a81612ba8565b6000602082840312156124d957600080fd5b815161065a81612ba8565b600080604083850312156124f757600080fd5b823561250281612ba8565b9150602083013561251281612ba8565b809150509250929050565b60008060006060848603121561253257600080fd5b833561253d81612ba8565b9250602084013561254d81612ba8565b929592945050506040919091013590565b6000806040838503121561257157600080fd5b823561257c81612ba8565b9150602083013561251281612bbd565b6000806040838503121561259f57600080fd5b82356125aa81612ba8565b946020939093013593505050565b600080600080608085870312156125ce57600080fd5b84356125d981612ba8565b93506020850135925060408501356125f081612ba8565b9150606085013561260081612bda565b939692955090935050565b60008060006060848603121561262057600080fd5b833561262b81612ba8565b95602085013595506040909401359392505050565b6000806020838503121561265357600080fd5b823567ffffffffffffffff81111561266a57600080fd5b6126768582860161245e565b90969095509350505050565b6000602080838503121561269557600080fd5b825167ffffffffffffffff808211156126ad57600080fd5b8185019150601f86818401126126c257600080fd5b8251828111156126d4576126d4612b92565b8060051b6126e3868201612a66565b8281528681019086880183880189018c10156126fe57600080fd5b600093505b8484101561278c5780518781111561271a57600080fd5b8801603f81018d1361272b57600080fd5b8981015160408982111561274157612741612b92565b6127528c601f198b85011601612a66565b8281528f8284860101111561276657600080fd5b612775838e8301848701612ac6565b865250505060019390930192918801918801612703565b509a9950505050505050505050565b6000602082840312156127ad57600080fd5b815161065a81612bbd565b6000602082840312156127ca57600080fd5b5051919050565b60008060008084860360c08112156127e857600080fd5b853594506080601f19820112156127fe57600080fd5b5060208501925060a085013567ffffffffffffffff81111561281f57600080fd5b61282b8782880161245e565b95989497509550505050565b60008060006060848603121561284c57600080fd5b8335925060208401359150604084013561286581612ba8565b809150509250925092565b60006020828403121561288257600080fd5b813561065a81612bcb565b60006020828403121561289f57600080fd5b815161065a81612bcb565b6000602082840312156128bc57600080fd5b815161065a81612bda565b600081518084526128df816020860160208601612ac6565b601f01601f19169290920160200192915050565b60008251612905818460208701612ac6565b9190910192915050565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b8281101561296457603f198886030184526129528583516128c7565b94509285019290850190600101612936565b5092979650505050505050565b60006020808301818452808551808352604092508286019150828160051b87010184880160005b838110156129e057888303603f19018552815180516001600160a01b031684528701518784018790526129cd878501826128c7565b9588019593505090860190600101612998565b509098975050505050505050565b60208152600061065a60208301846128c7565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112612a3657600080fd5b83018035915067ffffffffffffffff821115612a5157600080fd5b6020019150368190038213156124a357600080fd5b604051601f8201601f1916810167ffffffffffffffff81118282101715612a8f57612a8f612b92565b604052919050565b60008219821115612aaa57612aaa612b66565b500190565b600082821015612ac157612ac1612b66565b500390565b60005b83811015612ae1578181015183820152602001612ac9565b838111156104ec5750506000910152565b600181811c90821680612b0657607f821691505b60208210811415612b2757634e487b7160e01b600052602260045260246000fd5b50919050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff821415612b5f57612b5f612b66565b5060010190565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b03811681146115c657600080fd5b80151581146115c657600080fd5b60ff811681146115c657600080fd5b6bffffffffffffffffffffffff811681146115c657600080fdfe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a2646970667358221220bd23ca7a3b87962e0a1874f4570fe0e87661f2f4fe1e8fca5e3c793377f4e80864736f6c63430008060033608060405234801561001057600080fd5b506107dc806100206000396000f3fe608060405234801561001057600080fd5b506004361061004c5760003560e01c80633c78929e14610051578063909f1cad146100a3578063ac2293af146100b8578063de9443bf146100cb575b600080fd5b600054610081907401000000000000000000000000000000000000000090046bffffffffffffffffffffffff1681565b6040516bffffffffffffffffffffffff90911681526020015b60405180910390f35b6100b66100b136600461049e565b6100eb565b005b6100b66100c636600461049e565b610182565b6100de6100d9366004610429565b610234565b60405161009a919061051b565b60005473ffffffffffffffffffffffffffffffffffffffff16156101565760405162461bcd60e51b815260206004820152601760248201527f44656c65676174696f6e2f616c72656164792d696e697400000000000000000060448201526064015b60405180910390fd5b6bffffffffffffffffffffffff1674010000000000000000000000000000000000000000023317600055565b60005473ffffffffffffffffffffffffffffffffffffffff1633146101e95760405162461bcd60e51b815260206004820152601560248201527f44656c65676174696f6e2f6f6e6c792d6f776e65720000000000000000000000604482015260640161014d565b600080546bffffffffffffffffffffffff909216740100000000000000000000000000000000000000000273ffffffffffffffffffffffffffffffffffffffff909216919091179055565b60005460609073ffffffffffffffffffffffffffffffffffffffff16331461029e5760405162461bcd60e51b815260206004820152601560248201527f44656c65676174696f6e2f6f6e6c792d6f776e65720000000000000000000000604482015260640161014d565b8160008167ffffffffffffffff8111156102ba576102ba610790565b6040519080825280602002602001820160405280156102ed57816020015b60608152602001906001900390816102d85790505b5060408051808201909152600081526060602082015290915060005b83811015610382578686828181106103235761032361077a565b905060200281019061033591906105ae565b61033e9061063c565b91506103528260000151836020015161038d565b8382815181106103645761036461077a565b6020026020010181905250808061037a90610733565b915050610309565b509095945050505050565b60606000808473ffffffffffffffffffffffffffffffffffffffff166000856040516103b991906104ff565b60006040518083038185875af1925050503d80600081146103f6576040519150601f19603f3d011682016040523d82523d6000602084013e6103fb565b606091505b50915091508181906104205760405162461bcd60e51b815260040161014d919061059b565b50949350505050565b6000806020838503121561043c57600080fd5b823567ffffffffffffffff8082111561045457600080fd5b818501915085601f83011261046857600080fd5b81358181111561047757600080fd5b8660208260051b850101111561048c57600080fd5b60209290920196919550909350505050565b6000602082840312156104b057600080fd5b81356bffffffffffffffffffffffff811681146104cc57600080fd5b9392505050565b600081518084526104eb816020860160208601610703565b601f01601f19169290920160200192915050565b60008251610511818460208701610703565b9190910192915050565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b8281101561058e577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc088860301845261057c8583516104d3565b94509285019290850190600101610542565b5092979650505050505050565b6020815260006104cc60208301846104d3565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc183360301811261051157600080fd5b6040805190810167ffffffffffffffff8111828210171561060557610605610790565b60405290565b604051601f8201601f1916810167ffffffffffffffff8111828210171561063457610634610790565b604052919050565b60006040823603121561064e57600080fd5b6106566105e2565b823573ffffffffffffffffffffffffffffffffffffffff8116811461067a57600080fd5b815260208381013567ffffffffffffffff8082111561069857600080fd5b9085019036601f8301126106ab57600080fd5b8135818111156106bd576106bd610790565b6106cf84601f19601f8401160161060b565b915080825236848285010111156106e557600080fd5b80848401858401376000908201840152918301919091525092915050565b60005b8381101561071e578181015183820152602001610706565b8381111561072d576000848401525b50505050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82141561077357634e487b7160e01b600052601160045260246000fd5b5060010190565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052604160045260246000fdfea2646970667358221220e12294174ea821f82c2a1f83c4f824acb92d2de0e672f2347c9eb5611a26a93564736f6c63430008060033000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000062bb4fc73094c83b5e952c2180b23fa7054954c40000000000000000000000000000000000000000000000000000000000000023506f6f6c546f676574686572205374616b656420614f707455534443205469636b65740000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d73746b5054614f70745553444300000000000000000000000000000000000000

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106101c45760003560e01c8063889de805116100f9578063ac9650d811610097578063ca40edf111610071578063ca40edf114610418578063dd62ed3e14610460578063e18fa6eb14610499578063e7880ae1146104ac57600080fd5b8063ac9650d8146103d2578063adc9772e146103f2578063c2a672e01461040557600080fd5b806395d89b41116100d357806395d89b4114610391578063982b1f2f14610399578063a457c2d7146103ac578063a9059cbb146103bf57600080fd5b8063889de8051461032f5780638b4b4ec91461034257806390ab08851461035557600080fd5b80635f66501111610166578063666f7af611610140578063666f7af6146102b95780636c59f295146102cc5780636cc25db7146102df57806370a082311461030657600080fd5b80635f6650111461027157806363fc611f1461029c57806365a5d5f0146102af57600080fd5b806318160ddd116101a257806318160ddd1461021f57806323b872dd14610231578063313ce56714610244578063395093511461025e57600080fd5b806306452792146101c957806306fdde03146101de578063095ea7b3146101fc575b600080fd5b6101dc6101d73660046127d1565b6104bf565b005b6101e66104f2565b6040516101f391906129ee565b60405180910390f35b61020f61020a36600461258c565b610584565b60405190151581526020016101f3565b6002545b6040519081526020016101f3565b61020f61023f36600461251d565b61059b565b61024c610661565b60405160ff90911681526020016101f3565b61020f61026c36600461258c565b6106f9565b61028461027f36600461260b565b610735565b6040516001600160a01b0390911681526020016101f3565b600554610284906001600160a01b031681565b61022362ed4e0081565b6102846102c736600461260b565b6107b9565b6102846102da3660046125b8565b6108a3565b6102847f00000000000000000000000062bb4fc73094c83b5e952c2180b23fa7054954c481565b6102236103143660046124aa565b6001600160a01b031660009081526020819052604090205490565b61028461033d3660046125b8565b6109e9565b610284610350366004612837565b610ae4565b61020f6103633660046124e4565b6001600160a01b03918216600090815260066020908152604080832093909416825291909152205460ff1690565b6101e6610b4e565b6101dc6103a736600461255e565b610b5d565b61020f6103ba36600461258c565b610c3e565b61020f6103cd36600461258c565b610cef565b6103e56103e0366004612640565b610cfc565b6040516101f3919061290f565b6101dc61040036600461258c565b610d08565b6101dc61041336600461258c565b610d97565b61042b61042636600461258c565b610e24565b604080516001600160a01b0396871681529590941660208601529284019190915260608301521515608082015260a0016101f3565b61022361046e3660046124e4565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b6102846104a736600461260b565b611047565b6102846104ba36600461258c565b6110ed565b6104ec7f00000000000000000000000062bb4fc73094c83b5e952c2180b23fa7054954c4858585856110f9565b50505050565b60606003805461050190612af2565b80601f016020809104026020016040519081016040528092919081815260200182805461052d90612af2565b801561057a5780601f1061054f5761010080835404028352916020019161057a565b820191906000526020600020905b81548152906001019060200180831161055d57829003601f168201915b5050505050905090565b60006105913384846111d1565b5060015b92915050565b60006105a8848484611329565b6001600160a01b0384166000908152600160209081526040808320338452909152902054828110156106475760405162461bcd60e51b815260206004820152602860248201527f45524332303a207472616e7366657220616d6f756e742065786365656473206160448201527f6c6c6f77616e636500000000000000000000000000000000000000000000000060648201526084015b60405180910390fd5b61065485338584036111d1565b60019150505b9392505050565b60007f00000000000000000000000062bb4fc73094c83b5e952c2180b23fa7054954c46001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b1580156106bc57600080fd5b505afa1580156106d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106f4919061288d565b905090565b3360008181526001602090815260408083206001600160a01b03871684529091528120549091610591918590610730908690612a97565b6111d1565b600061074084611540565b600061074c85856115c9565b905061075981308561161f565b6107638584611641565b336001600160a01b031684866001600160a01b03167f6862a473baa6176f1c866c69aa93da8508d7afc71b52dddc9d5e8b0bb7aab6f4866040516107a991815260200190565b60405180910390a4949350505050565b60006001600160a01b0384166108115760405162461bcd60e51b815260206004820181905260248201527f5457414244656c656761746f722f646c6774722d6e6f742d7a65726f2d616472604482015260640161063e565b61081a82611719565b600061082685856115c9565b905061085d6001600160a01b037f00000000000000000000000062bb4fc73094c83b5e952c2180b23fa7054954c416338386611769565b336001600160a01b031684866001600160a01b03167f383183291bd9a7fb8bd9c7c86c5013a89d1490c9f4e486da279804b83729a1dc866040516107a991815260200190565b60006108ae85611540565b6108b78361181a565b6108ce826bffffffffffffffffffffffff16611870565b60006108da86866115c9565b90506108e5816118c3565b4283016bffffffffffffffffffffffff84161561097d576040517fac2293af0000000000000000000000000000000000000000000000000000000081526bffffffffffffffffffffffff821660048201526001600160a01b0383169063ac2293af90602401600060405180830381600087803b15801561096457600080fd5b505af1158015610978573d6000803e3d6000fd5b505050505b6109878286611991565b604080516bffffffffffffffffffffffff831681523360208201526001600160a01b03808816928992918b16917ffd96a87f22afea1e17a7117a4923f1499a1c1eb2bd7c492caf07f3a3c38ade6f910160405180910390a45095945050505050565b60006109f485611540565b6109fd8361181a565b610a14826bffffffffffffffffffffffff16611870565b4282016000610a6e610a6888886040516bffffffffffffffffffffffff19606084901b1660208201526034810182905260009060540160405160208183030381529060405280519060200120905092915050565b83611a17565b9050610a7a8186611991565b604080516bffffffffffffffffffffffff841681526001600160a01b03838116602083015233828401529151878316928992908b16917f5533acb96061e404278604d3df68397263be1d4b9df394136a2968802633d8a59181900360600190a49695505050505050565b6000610aef82611abd565b6000610afb33866115c9565b9050610b0881848661161f565b826001600160a01b031685336001600160a01b03167f622b7da8a20026f1176ccc7ec0a635a4544a67e99b0125018e3d89b888ce8ebe876040516107a991815260200190565b60606004805461050190612af2565b6001600160a01b038216610bb35760405162461bcd60e51b815260206004820152601f60248201527f5457414244656c656761746f722f7265702d6e6f742d7a65726f2d6164647200604482015260640161063e565b3360008181526006602090815260408083206001600160a01b0387168085529083529281902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001686151590811790915590519081529192917f50062a33e55b9f3dfcf05fbf1356b7c92313796cfb8526cdee5a497fcbb8cc3391015b60405180910390a35050565b3360009081526001602090815260408083206001600160a01b038616845290915281205482811015610cd85760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f7760448201527f207a65726f000000000000000000000000000000000000000000000000000000606482015260840161063e565b610ce533858584036111d1565b5060019392505050565b6000610591338484611329565b606061065a8383611b13565b610d1181611719565b610d466001600160a01b037f00000000000000000000000062bb4fc73094c83b5e952c2180b23fa7054954c416333084611769565b610d508282611641565b816001600160a01b03167f15c2c6e5db9e25d828754c9c5cee6c5c3df074e6ac26e7491c0f8ce7bbd447d682604051610d8b91815260200190565b60405180910390a25050565b610da082611abd565b610da981611719565b610db33382611c0d565b610de76001600160a01b037f00000000000000000000000062bb4fc73094c83b5e952c2180b23fa7054954c4168383611d92565b6040518181526001600160a01b0383169033907ff7128607975b3ff61bc02d19a1c8267e526f7a5b7144dc4efcdac634663fd36990602001610c32565b6000806000806000610e3687876115c9565b94506001600160a01b0385163b15156040517f8d22ea2a0000000000000000000000000000000000000000000000000000000081526001600160a01b0387811660048301529192507f00000000000000000000000062bb4fc73094c83b5e952c2180b23fa7054954c490911690638d22ea2a9060240160206040518083038186803b158015610ec457600080fd5b505afa158015610ed8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610efc91906124c7565b6040517f70a082310000000000000000000000000000000000000000000000000000000081526001600160a01b0387811660048301529195507f00000000000000000000000062bb4fc73094c83b5e952c2180b23fa7054954c4909116906370a082319060240160206040518083038186803b158015610f7b57600080fd5b505afa158015610f8f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fb391906127b8565b9250801561103d57846001600160a01b0316633c78929e6040518163ffffffff1660e01b815260040160206040518083038186803b158015610ff457600080fd5b505afa158015611008573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061102c91906128aa565b6bffffffffffffffffffffffff1691505b9295509295909350565b600061105284611540565b61105b82611719565b600061106785856115c9565b90506110738584611c0d565b6110a76001600160a01b037f00000000000000000000000062bb4fc73094c83b5e952c2180b23fa7054954c4168285611d92565b336001600160a01b031684866001600160a01b03167fb1968721eeb35d2206c8aa91805bc908019965ff4cff13c158f89956fb8e9248866040516107a991815260200190565b600061065a83836115c9565b6001600160a01b03851663d505accf333087873561111d60408a0160208b01612870565b604080517fffffffff0000000000000000000000000000000000000000000000000000000060e089901b1681526001600160a01b0396871660048201529590941660248601526044850192909252606484015260ff16608483015286013560a4820152606086013560c482015260e401600060405180830381600087803b1580156111a757600080fd5b505af11580156111bb573d6000803e3d6000fd5b505050506111c98282611b13565b505050505050565b6001600160a01b03831661124c5760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f2061646460448201527f7265737300000000000000000000000000000000000000000000000000000000606482015260840161063e565b6001600160a01b0382166112c85760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f20616464726560448201527f7373000000000000000000000000000000000000000000000000000000000000606482015260840161063e565b6001600160a01b0383811660008181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050565b6001600160a01b0383166113a55760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f20616460448201527f6472657373000000000000000000000000000000000000000000000000000000606482015260840161063e565b6001600160a01b0382166114215760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201527f6573730000000000000000000000000000000000000000000000000000000000606482015260840161063e565b6001600160a01b038316600090815260208190526040902054818110156114b05760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e742065786365656473206260448201527f616c616e63650000000000000000000000000000000000000000000000000000606482015260840161063e565b6001600160a01b038085166000908152602081905260408082208585039055918516815290812080548492906114e7908490612a97565b92505081905550826001600160a01b0316846001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8460405161153391815260200190565b60405180910390a36104ec565b6001600160a01b03811633148061157a57506001600160a01b038116600090815260066020908152604080832033845290915290205460ff165b6115c65760405162461bcd60e51b815260206004820152601e60248201527f5457414244656c656761746f722f6e6f742d646c6774722d6f722d7265700000604482015260640161063e565b50565b600061065a61161a84846040516bffffffffffffffffffffffff19606084901b1660208201526034810182905260009060540160405160208183030381529060405280519060200120905092915050565b611ddb565b61162881611719565b611631836118c3565b61163c838383611e64565b505050565b6001600160a01b0382166116975760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f206164647265737300604482015260640161063e565b80600260008282546116a99190612a97565b90915550506001600160a01b038216600090815260208190526040812080548392906116d6908490612a97565b90915550506040518181526001600160a01b038316906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602001610c32565b600081116115c65760405162461bcd60e51b815260206004820152601c60248201527f5457414244656c656761746f722f616d6f756e742d67742d7a65726f00000000604482015260640161063e565b6040516001600160a01b03808516602483015283166044820152606481018290526104ec9085907f23b872dd00000000000000000000000000000000000000000000000000000000906084015b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090931692909217909152611ee7565b6001600160a01b0381166115c65760405162461bcd60e51b815260206004820181905260248201527f5457414244656c656761746f722f646c67742d6e6f742d7a65726f2d61646472604482015260640161063e565b62ed4e008111156115c65760405162461bcd60e51b815260206004820152601b60248201527f5457414244656c656761746f722f6c6f636b2d746f6f2d6c6f6e670000000000604482015260640161063e565b806001600160a01b0316633c78929e6040518163ffffffff1660e01b815260040160206040518083038186803b1580156118fc57600080fd5b505afa158015611910573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061193491906128aa565b6bffffffffffffffffffffffff164210156115c65760405162461bcd60e51b815260206004820152601f60248201527f5457414244656c656761746f722f64656c65676174696f6e2d6c6f636b656400604482015260640161063e565b604080516001600160a01b0383166024808301919091528251808303909101815260449091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f5c19a95c0000000000000000000000000000000000000000000000000000000090811790915290611a108482611fcc565b5050505050565b6005546000908190611a32906001600160a01b031685612110565b6040517f909f1cad0000000000000000000000000000000000000000000000000000000081526bffffffffffffffffffffffff851660048201529091506001600160a01b0382169063909f1cad90602401600060405180830381600087803b158015611a9d57600080fd5b505af1158015611ab1573d6000803e3d6000fd5b50929695505050505050565b6001600160a01b0381166115c65760405162461bcd60e51b815260206004820152601e60248201527f5457414244656c656761746f722f746f2d6e6f742d7a65726f2d616464720000604482015260640161063e565b60608160008167ffffffffffffffff811115611b3157611b31612b92565b604051908082528060200260200182016040528015611b6457816020015b6060815260200190600190039081611b4f5790505b50905060005b82811015611c0457611bd430878784818110611b8857611b88612b7c565b9050602002810190611b9a9190612a01565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506121c792505050565b828281518110611be657611be6612b7c565b60200260200101819052508080611bfc90612b2d565b915050611b6a565b50949350505050565b6001600160a01b038216611c895760405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f2061646472657360448201527f7300000000000000000000000000000000000000000000000000000000000000606482015260840161063e565b6001600160a01b03821660009081526020819052604090205481811015611d185760405162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e60448201527f6365000000000000000000000000000000000000000000000000000000000000606482015260840161063e565b6001600160a01b0383166000908152602081905260408120838303905560028054849290611d47908490612aaf565b90915550506040518281526000906001600160a01b038516907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a3505050565b6040516001600160a01b03831660248201526044810182905261163c9084907fa9059cbb00000000000000000000000000000000000000000000000000000000906064016117b6565b600554600090610595906001600160a01b031683306040517f3d602d80600a3d3981f3363d3d373d3d3d363d730000000000000000000000008152606093841b60148201527f5af43d82803e903d91602b57fd5bf3ff000000000000000000000000000000006028820152921b6038830152604c8201526037808220606c830152605591012090565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb00000000000000000000000000000000000000000000000000000000908117909152906111c98582611fcc565b6000611f3c826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166121ec9092919063ffffffff16565b80519091501561163c5780806020019051810190611f5a919061279b565b61163c5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f74207375636365656400000000000000000000000000000000000000000000606482015260840161063e565b60408051600180825281830190925260609160009190816020015b604080518082019091526000815260606020820152815260200190600190039081611fe757905050905060405180604001604052807f00000000000000000000000062bb4fc73094c83b5e952c2180b23fa7054954c46001600160a01b03168152602001848152508160008151811061206257612062612b7c565b60209081029190910101526040517fde9443bf0000000000000000000000000000000000000000000000000000000081526001600160a01b0385169063de9443bf906120b2908490600401612971565b600060405180830381600087803b1580156120cc57600080fd5b505af11580156120e0573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526121089190810190612682565b949350505050565b60006040517f3d602d80600a3d3981f3363d3d373d3d3d363d7300000000000000000000000081528360601b60148201527f5af43d82803e903d91602b57fd5bf300000000000000000000000000000000006028820152826037826000f59150506001600160a01b0381166105955760405162461bcd60e51b815260206004820152601760248201527f455243313136373a2063726561746532206661696c6564000000000000000000604482015260640161063e565b606061065a8383604051806060016040528060278152602001612bf5602791396121fb565b606061210884846000856122e6565b6060833b6122715760405162461bcd60e51b815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f60448201527f6e74726163740000000000000000000000000000000000000000000000000000606482015260840161063e565b600080856001600160a01b03168560405161228c91906128f3565b600060405180830381855af49150503d80600081146122c7576040519150601f19603f3d011682016040523d82523d6000602084013e6122cc565b606091505b50915091506122dc828286612425565b9695505050505050565b60608247101561235e5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c0000000000000000000000000000000000000000000000000000606482015260840161063e565b843b6123ac5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640161063e565b600080866001600160a01b031685876040516123c891906128f3565b60006040518083038185875af1925050503d8060008114612405576040519150601f19603f3d011682016040523d82523d6000602084013e61240a565b606091505b509150915061241a828286612425565b979650505050505050565b6060831561243457508161065a565b8251156124445782518084602001fd5b8160405162461bcd60e51b815260040161063e91906129ee565b60008083601f84011261247057600080fd5b50813567ffffffffffffffff81111561248857600080fd5b6020830191508360208260051b85010111156124a357600080fd5b9250929050565b6000602082840312156124bc57600080fd5b813561065a81612ba8565b6000602082840312156124d957600080fd5b815161065a81612ba8565b600080604083850312156124f757600080fd5b823561250281612ba8565b9150602083013561251281612ba8565b809150509250929050565b60008060006060848603121561253257600080fd5b833561253d81612ba8565b9250602084013561254d81612ba8565b929592945050506040919091013590565b6000806040838503121561257157600080fd5b823561257c81612ba8565b9150602083013561251281612bbd565b6000806040838503121561259f57600080fd5b82356125aa81612ba8565b946020939093013593505050565b600080600080608085870312156125ce57600080fd5b84356125d981612ba8565b93506020850135925060408501356125f081612ba8565b9150606085013561260081612bda565b939692955090935050565b60008060006060848603121561262057600080fd5b833561262b81612ba8565b95602085013595506040909401359392505050565b6000806020838503121561265357600080fd5b823567ffffffffffffffff81111561266a57600080fd5b6126768582860161245e565b90969095509350505050565b6000602080838503121561269557600080fd5b825167ffffffffffffffff808211156126ad57600080fd5b8185019150601f86818401126126c257600080fd5b8251828111156126d4576126d4612b92565b8060051b6126e3868201612a66565b8281528681019086880183880189018c10156126fe57600080fd5b600093505b8484101561278c5780518781111561271a57600080fd5b8801603f81018d1361272b57600080fd5b8981015160408982111561274157612741612b92565b6127528c601f198b85011601612a66565b8281528f8284860101111561276657600080fd5b612775838e8301848701612ac6565b865250505060019390930192918801918801612703565b509a9950505050505050505050565b6000602082840312156127ad57600080fd5b815161065a81612bbd565b6000602082840312156127ca57600080fd5b5051919050565b60008060008084860360c08112156127e857600080fd5b853594506080601f19820112156127fe57600080fd5b5060208501925060a085013567ffffffffffffffff81111561281f57600080fd5b61282b8782880161245e565b95989497509550505050565b60008060006060848603121561284c57600080fd5b8335925060208401359150604084013561286581612ba8565b809150509250925092565b60006020828403121561288257600080fd5b813561065a81612bcb565b60006020828403121561289f57600080fd5b815161065a81612bcb565b6000602082840312156128bc57600080fd5b815161065a81612bda565b600081518084526128df816020860160208601612ac6565b601f01601f19169290920160200192915050565b60008251612905818460208701612ac6565b9190910192915050565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b8281101561296457603f198886030184526129528583516128c7565b94509285019290850190600101612936565b5092979650505050505050565b60006020808301818452808551808352604092508286019150828160051b87010184880160005b838110156129e057888303603f19018552815180516001600160a01b031684528701518784018790526129cd878501826128c7565b9588019593505090860190600101612998565b509098975050505050505050565b60208152600061065a60208301846128c7565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112612a3657600080fd5b83018035915067ffffffffffffffff821115612a5157600080fd5b6020019150368190038213156124a357600080fd5b604051601f8201601f1916810167ffffffffffffffff81118282101715612a8f57612a8f612b92565b604052919050565b60008219821115612aaa57612aaa612b66565b500190565b600082821015612ac157612ac1612b66565b500390565b60005b83811015612ae1578181015183820152602001612ac9565b838111156104ec5750506000910152565b600181811c90821680612b0657607f821691505b60208210811415612b2757634e487b7160e01b600052602260045260246000fd5b50919050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff821415612b5f57612b5f612b66565b5060010190565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b03811681146115c657600080fd5b80151581146115c657600080fd5b60ff811681146115c657600080fd5b6bffffffffffffffffffffffff811681146115c657600080fdfe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a2646970667358221220bd23ca7a3b87962e0a1874f4570fe0e87661f2f4fe1e8fca5e3c793377f4e80864736f6c63430008060033

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

000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000062bb4fc73094c83b5e952c2180b23fa7054954c40000000000000000000000000000000000000000000000000000000000000023506f6f6c546f676574686572205374616b656420614f707455534443205469636b65740000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d73746b5054614f70745553444300000000000000000000000000000000000000

-----Decoded View---------------
Arg [0] : name_ (string): PoolTogether Staked aOptUSDC Ticket
Arg [1] : symbol_ (string): stkPTaOptUSDC
Arg [2] : _ticket (address): 0x62BB4fc73094c83B5e952C2180B23fA7054954c4

-----Encoded View---------------
8 Constructor Arguments found :
Arg [0] : 0000000000000000000000000000000000000000000000000000000000000060
Arg [1] : 00000000000000000000000000000000000000000000000000000000000000c0
Arg [2] : 00000000000000000000000062bb4fc73094c83b5e952c2180b23fa7054954c4
Arg [3] : 0000000000000000000000000000000000000000000000000000000000000023
Arg [4] : 506f6f6c546f676574686572205374616b656420614f70745553444320546963
Arg [5] : 6b65740000000000000000000000000000000000000000000000000000000000
Arg [6] : 000000000000000000000000000000000000000000000000000000000000000d
Arg [7] : 73746b5054614f70745553444300000000000000000000000000000000000000


Block Transaction Difficulty Gas Used Reward
Block Uncle Number Difficulty Gas Used Reward
Loading
Loading
Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for interesting conversations.