Contract 0x3f6Bcd0761871CE301cac4C7984B0105aDB737Ce

Contract Overview

Balance:
0.020428428358208953 MATIC
Txn Hash
Method
Block
From
To
Value [Txn Fee]
0xf05761bef287fe1cf58e9867600ba4e32d021f0f2ffbb1e6e74d347cf4dafd33Set Paused Token291994012022-11-17 13:40:54130 days 22 hrs ago0x2c305888a456e7004663bc12a74395e637eabcbc IN  0x3f6bcd0761871ce301cac4c7984b0105adb737ce0 MATIC0.0000369186991.299999996
0x07129a5396fa020ecd77923536a6d563873566a84ac63fc92af0372e8587b738Withdraw291994012022-11-17 13:40:54130 days 22 hrs ago0x2c305888a456e7004663bc12a74395e637eabcbc IN  0x3f6bcd0761871ce301cac4c7984b0105adb737ce0 MATIC0.0001035813991.299999996
0xe6b9377393fa600dc8b476afe5dd23c0eff230992cd8143dd3f9fa056bc26439Set Paused Token291993992022-11-17 13:40:34130 days 22 hrs ago0x2c305888a456e7004663bc12a74395e637eabcbc IN  0x3f6bcd0761871ce301cac4c7984b0105adb737ce0 MATIC0.0000405586991.299999997
0xb8bd0585ebf140deb759a9938135c2984293eb751df8ede13311def202c4af8bWithdraw291993992022-11-17 13:40:34130 days 22 hrs ago0x2c305888a456e7004663bc12a74395e637eabcbc IN  0x3f6bcd0761871ce301cac4c7984b0105adb737ce0 MATIC0.0000953237991.299999997
0xe1de1f9ca75d216c2c24fd769aa78cfaa58249c6e92c02a5933a1f4ac2d291e6Set Paused Token291993972022-11-17 13:40:14130 days 22 hrs ago0x2c305888a456e7004663bc12a74395e637eabcbc IN  0x3f6bcd0761871ce301cac4c7984b0105adb737ce0 MATIC0.0000402466991.299999998
0x00560f4ed728aa5bb484b36ae76caa8a15a3f7ea49eff9754f656d22874b94beWithdraw House E...291993972022-11-17 13:40:14130 days 22 hrs ago0x2c305888a456e7004663bc12a74395e637eabcbc IN  0x3f6bcd0761871ce301cac4c7984b0105adb737ce0 MATIC0.0000571375991.299999998
0xee932e56d151737fc2f0ed9a23ea728bf7988c399bba18ffea6baa7a33d28085Withdraw House E...280100892022-09-09 9:08:41200 days 2 hrs ago0x2c305888a456e7004663bc12a74395e637eabcbc IN  0x3f6bcd0761871ce301cac4c7984b0105adb737ce0 MATIC0.000065928001 1.500000024
0x818daff6676c183635a00fa4ca9f2eb2c052e3bc09b3ad35c0ed75250b29105fDeposit280092582022-09-09 7:59:13200 days 3 hrs ago0x2c305888a456e7004663bc12a74395e637eabcbc IN  0x3f6bcd0761871ce301cac4c7984b0105adb737ce0 MATIC0.0000972915011.500000016
0x2bd946bd75e7dd73c9aae051c8f12b7894bd8eecd6f6cee67a1086a71eb6b996Deposit280092502022-09-09 7:58:33200 days 3 hrs ago0x2c305888a456e7004663bc12a74395e637eabcbc IN  0x3f6bcd0761871ce301cac4c7984b0105adb737ce97 MATIC0.0000417181.500000015
0xa5f3f3c6db2997a9f4ea771b55f003361cd80e9230ce3b43486161b214fc81afSet Paused Token280090382022-09-09 7:40:50200 days 4 hrs ago0x2c305888a456e7004663bc12a74395e637eabcbc IN  0x3f6bcd0761871ce301cac4c7984b0105adb737ce0 MATIC0.00004679851.500000011
0x5b4869dec43c82c130b6a3efaa2964ae741841617d42dba5377d2833b96fd454Set House Edge S...280089932022-09-09 7:37:04200 days 4 hrs ago0x2c305888a456e7004663bc12a74395e637eabcbc IN  0x3f6bcd0761871ce301cac4c7984b0105adb737ce0 MATIC0.0000779940011.500000023
0x30dfb23c8b531345f47ee88f014e20aae4ce0fc1fd766cc0b391b4e55184872fSet Token VRF Su...280089742022-09-09 7:35:29200 days 4 hrs ago0x2c305888a456e7004663bc12a74395e637eabcbc IN  0x3f6bcd0761871ce301cac4c7984b0105adb737ce0 MATIC0.0000467641.50000003
0x635421b1488442d19d07cfd1c78b3b51ffb4c574440f25d8d47a54a6689beab3Set Balance Risk280089712022-09-09 7:35:14200 days 4 hrs ago0x2c305888a456e7004663bc12a74395e637eabcbc IN  0x3f6bcd0761871ce301cac4c7984b0105adb737ce0 MATIC0.00004677751.50000003
0x9ad6e26aa6794fcc41a2341cacab4de57300fed5431bfac879741f0864253492Set Allowed Toke...280089692022-09-09 7:35:04200 days 4 hrs ago0x2c305888a456e7004663bc12a74395e637eabcbc IN  0x3f6bcd0761871ce301cac4c7984b0105adb737ce0 MATIC0.0000721065011.500000034
0x6fdd081ef77881c52f9abe29d842b4b5c352703d241fe43b99a13ac68a2cc4caAdd Token280089672022-09-09 7:34:54200 days 4 hrs ago0x2c305888a456e7004663bc12a74395e637eabcbc IN  0x3f6bcd0761871ce301cac4c7984b0105adb737ce0 MATIC0.0000870720021.500000036
0x2957ee8b7ca0504059a93fa85ce3962e8cb136db1ce19c412f9a9ec4f27363c0Set House Edge S...280089582022-09-09 7:34:08200 days 4 hrs ago0x2c305888a456e7004663bc12a74395e637eabcbc IN  0x3f6bcd0761871ce301cac4c7984b0105adb737ce0 MATIC0.0000779580011.500000029
0x20e70a97866ae6ad1986e33d97d79a24584556d653d62cb16dd40aeccfddc919Set Token VRF Su...280089492022-09-09 7:33:23200 days 4 hrs ago0x2c305888a456e7004663bc12a74395e637eabcbc IN  0x3f6bcd0761871ce301cac4c7984b0105adb737ce0 MATIC0.0000467641.500000023
0x45a59c0cac079381071d6cfb30012e9a35709481898ec70e317a43daa8615344Set Balance Risk280089482022-09-09 7:33:18200 days 4 hrs ago0x2c305888a456e7004663bc12a74395e637eabcbc IN  0x3f6bcd0761871ce301cac4c7984b0105adb737ce0 MATIC0.00004677751.500000023
0x42c6dd3fee725b99f4622f79d76f4f85d96ed41f5a322af1d3e4ce4fb85e92c6Set Allowed Toke...280089462022-09-09 7:33:08200 days 4 hrs ago0x2c305888a456e7004663bc12a74395e637eabcbc IN  0x3f6bcd0761871ce301cac4c7984b0105adb737ce0 MATIC0.0000721065011.500000022
0x4e40840ca742bed0540d6585bb8f2382f55eedd24fd4aa0fb773766608a4d18fAdd Token280089452022-09-09 7:33:03200 days 4 hrs ago0x2c305888a456e7004663bc12a74395e637eabcbc IN  0x3f6bcd0761871ce301cac4c7984b0105adb737ce0 MATIC0.0000833700011.500000023
0x67b61afdc6cad750be6411dee04634c9a80368aed1dabbfad0f6e0dc5d58b7ceSet House Edge S...280089412022-09-09 7:32:43200 days 4 hrs ago0x2c305888a456e7004663bc12a74395e637eabcbc IN  0x3f6bcd0761871ce301cac4c7984b0105adb737ce0 MATIC0.0000775980011.500000021
0xbb39d2d8880cfac31db36b730c39ade019eb8f67f49cc01fb916d7840758b0f3Set Token VRF Su...280089342022-09-09 7:32:08200 days 4 hrs ago0x2c305888a456e7004663bc12a74395e637eabcbc IN  0x3f6bcd0761871ce301cac4c7984b0105adb737ce0 MATIC0.0000464041.500000018
0x0a6bfc9475616567c0555e735372a29a574cd949f8e645cdfb0d4e86a6d66638Set Balance Risk280089332022-09-09 7:32:03200 days 4 hrs ago0x2c305888a456e7004663bc12a74395e637eabcbc IN  0x3f6bcd0761871ce301cac4c7984b0105adb737ce0 MATIC0.00004641751.500000018
0x52df2b28cc4af1d77127aa76dd87e5e956042f8561fbe4441b7d18d2f78b8ab8Set Allowed Toke...280089322022-09-09 7:31:58200 days 4 hrs ago0x2c305888a456e7004663bc12a74395e637eabcbc IN  0x3f6bcd0761871ce301cac4c7984b0105adb737ce0 MATIC0.00007174651.500000017
0xc657926bafd19d130f61cc6e060eb2e0ae5d7147b85c1480b8540db6d94a2d55Add Token280089302022-09-09 7:31:48200 days 4 hrs ago0x2c305888a456e7004663bc12a74395e637eabcbc IN  0x3f6bcd0761871ce301cac4c7984b0105adb737ce0 MATIC0.0000492331.500000017
[ Download CSV Export 
Latest 25 internal transaction
Parent Txn Hash Block From To Value
0xb8bd0585ebf140deb759a9938135c2984293eb751df8ede13311def202c4af8b291993992022-11-17 13:40:34130 days 22 hrs ago 0x3f6bcd0761871ce301cac4c7984b0105adb737ce0x2c305888a456e7004663bc12a74395e637eabcbc98.97585451298006296 MATIC
0x00560f4ed728aa5bb484b36ae76caa8a15a3f7ea49eff9754f656d22874b94be291993972022-11-17 13:40:14130 days 22 hrs ago 0x3f6bcd0761871ce301cac4c7984b0105adb737ce 0x8343f2dc53e55dbbe626b9d8b4fbbb4e37dc218b0.006209476119402983 MATIC
0x00560f4ed728aa5bb484b36ae76caa8a15a3f7ea49eff9754f656d22874b94be291993972022-11-17 13:40:14130 days 22 hrs ago 0x3f6bcd0761871ce301cac4c7984b0105adb737ce0x2c305888a456e7004663bc12a74395e637eabcbc0.012418952238805968 MATIC
0xaa2b3dac19d356f4bdc7e57baf5d8d8d135bde822b45764a83b0b3b5eaf3ed84291310552022-11-13 12:53:15134 days 22 hrs ago 0x3f6bcd0761871ce301cac4c7984b0105adb737ce0x68d99e952cf3d4faaa6411c1953979f54552a8f70.950309 MATIC
0xaa2b3dac19d356f4bdc7e57baf5d8d8d135bde822b45764a83b0b3b5eaf3ed84291310552022-11-13 12:53:15134 days 22 hrs ago 0xc7fe24211cf3edc2e403b96e338ff52c4bd0c1ce 0x3f6bcd0761871ce301cac4c7984b0105adb737ce0.029391 MATIC
0x61166b17e96de624aae57cd05efe44b6491440fc7a5392c0151f76f7a98aff1f291310422022-11-13 12:52:10134 days 22 hrs ago 0xc7fe24211cf3edc2e403b96e338ff52c4bd0c1ce 0x3f6bcd0761871ce301cac4c7984b0105adb737ce0.9797 MATIC
0x400be7ab62ceffd69bc6b05a254124d16bae7603b1922ca179b3c236674c36ab291310322022-11-13 12:51:20134 days 22 hrs ago 0xc7fe24211cf3edc2e403b96e338ff52c4bd0c1ce 0x3f6bcd0761871ce301cac4c7984b0105adb737ce0.9797 MATIC
0x1d251fc721a43ac4cb21ec6b2aab10972da94c39e4459b841f0311578ff9ec99290750602022-11-10 6:52:25138 days 4 hrs ago 0xc6bde2d979f44b05cdbf90b0eb335c37ed9a4081 0x3f6bcd0761871ce301cac4c7984b0105adb737ce0.25560864830344604 MATIC
0x8751f9400f2df6c89aaeff5c8221b3a01a9b8cc0627741752ff4a09d54280b65288634782022-10-29 0:01:25150 days 11 hrs ago 0x965abc32a02235b29814213ec17e255b1cabf2a0 0x3f6bcd0761871ce301cac4c7984b0105adb737ce0.1 MATIC
0xbdcfab00fa69a70317cc856c374f67a40b03ffff1fc0af11c908e08facdedba0288633102022-10-28 23:47:23150 days 12 hrs ago 0x3f6bcd0761871ce301cac4c7984b0105adb737ce0xde6d7b739699c109f25cd017b0f6faeef7da335e0.0097 MATIC
0xbdcfab00fa69a70317cc856c374f67a40b03ffff1fc0af11c908e08facdedba0288633102022-10-28 23:47:23150 days 12 hrs ago 0xc7fe24211cf3edc2e403b96e338ff52c4bd0c1ce 0x3f6bcd0761871ce301cac4c7984b0105adb737ce0.0003 MATIC
0xa23571557791c816297609702a0d494cf3f12703615f8719b134fcc87a9fffea288632152022-10-28 23:39:26150 days 12 hrs ago 0xc6bde2d979f44b05cdbf90b0eb335c37ed9a4081 0x3f6bcd0761871ce301cac4c7984b0105adb737ce0.01 MATIC
0x8d814f2d066e964600004136881c9e16bf13fbfd7efde371de9e59d34363d0da288632042022-10-28 23:38:31150 days 12 hrs ago 0xc6bde2d979f44b05cdbf90b0eb335c37ed9a4081 0x3f6bcd0761871ce301cac4c7984b0105adb737ce0.01 MATIC
0xd50e94116be4bbbee5b805f2d9235f8c9ce504fa961de05ae17225147047532a288631942022-10-28 23:37:41150 days 12 hrs ago 0xc6bde2d979f44b05cdbf90b0eb335c37ed9a4081 0x3f6bcd0761871ce301cac4c7984b0105adb737ce0.01 MATIC
0xe73a32487f91a7deab8f90cbd0196e38fc34c9163b0f60109aff922c0a8262ff286852642022-10-18 15:38:31160 days 20 hrs ago 0x3f6bcd0761871ce301cac4c7984b0105adb737ce0xf4ee4149559ef4b8c6cef656857e6307566b556d0.004777611940298508 MATIC
0xe73a32487f91a7deab8f90cbd0196e38fc34c9163b0f60109aff922c0a8262ff286852642022-10-18 15:38:31160 days 20 hrs ago 0xc6bde2d979f44b05cdbf90b0eb335c37ed9a4081 0x3f6bcd0761871ce301cac4c7984b0105adb737ce0.0003 MATIC
0x0eb09037b9d63a1b1a6263e2e76753970e2089f140ea5d4380f2b9efd4d51293286852372022-10-18 15:36:15160 days 20 hrs ago 0xc6bde2d979f44b05cdbf90b0eb335c37ed9a4081 0x3f6bcd0761871ce301cac4c7984b0105adb737ce0.01 MATIC
0x0385d2642d8bf3ceb624655df4d8a5af3ede84a1a78d595288c7cb370592e7c6285644932022-10-11 15:18:45167 days 20 hrs ago 0xc6bde2d979f44b05cdbf90b0eb335c37ed9a4081 0x3f6bcd0761871ce301cac4c7984b0105adb737ce0.0195 MATIC
0xb6ca9258d47e61b4103264cc7a8381d1a4781ca31a9b60c69669cff1fcf0bfd0281511792022-09-17 13:46:02191 days 22 hrs ago 0x965abc32a02235b29814213ec17e255b1cabf2a0 0x3f6bcd0761871ce301cac4c7984b0105adb737ce0.01 MATIC
0xbd6a762e681b424c2fe2a8c8213e44fafda4ab14f0ce232d94e959115961cb01281511682022-09-17 13:45:07191 days 22 hrs ago 0x965abc32a02235b29814213ec17e255b1cabf2a0 0x3f6bcd0761871ce301cac4c7984b0105adb737ce0.01 MATIC
0x0d3f6cae2ecd4086f93fcdb849574b0a5c8bdfdc82f3e52c264ee4f2aba26b4b281511562022-09-17 13:44:06191 days 22 hrs ago 0x965abc32a02235b29814213ec17e255b1cabf2a0 0x3f6bcd0761871ce301cac4c7984b0105adb737ce0.01 MATIC
0x29aeb70ff87537dc7e64742e2b7cdc9681b48e49de06633a287a02010f1f382d281511462022-09-17 13:43:16191 days 22 hrs ago 0x965abc32a02235b29814213ec17e255b1cabf2a0 0x3f6bcd0761871ce301cac4c7984b0105adb737ce0.01 MATIC
0x230589bebe27450b6bf1a30f1fe59f63fef69e674c8a3a1a592a179b386f32b9281510172022-09-17 13:32:29191 days 22 hrs ago 0x3f6bcd0761871ce301cac4c7984b0105adb737ce0x2c305888a456e7004663bc12a74395e637eabcbc0.020270833333333334 MATIC
0x230589bebe27450b6bf1a30f1fe59f63fef69e674c8a3a1a592a179b386f32b9281510172022-09-17 13:32:29191 days 22 hrs ago 0x965abc32a02235b29814213ec17e255b1cabf2a0 0x3f6bcd0761871ce301cac4c7984b0105adb737ce0.00027 MATIC
0x6a5e3ff277b3a8d07a21102eb6fbce0aff4c0d0df8de56caa0176b71ed5ddb42281510062022-09-17 13:31:34191 days 22 hrs ago 0x965abc32a02235b29814213ec17e255b1cabf2a0 0x3f6bcd0761871ce301cac4c7984b0105adb737ce0.01 MATIC
[ 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:
Bank

Compiler Version
v0.8.16+commit.07a7930e

Optimization Enabled:
Yes with 8000 runs

Other Settings:
default evmVersion, MIT license
File 1 of 19 : KeeperBase.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract KeeperBase {
  error OnlySimulatedBackend();

  /**
   * @notice method that allows it to be simulated via eth_call by checking that
   * the sender is the zero address.
   */
  function preventExecution() internal view {
    if (tx.origin != address(0)) {
      revert OnlySimulatedBackend();
    }
  }

  /**
   * @notice modifier that allows it to be simulated via eth_call by checking
   * that the sender is the zero address.
   */
  modifier cannotExecute() {
    preventExecution();
    _;
  }
}

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

import "./KeeperBase.sol";
import "./interfaces/KeeperCompatibleInterface.sol";

abstract contract KeeperCompatible is KeeperBase, KeeperCompatibleInterface {}

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

interface KeeperCompatibleInterface {
  /**
   * @notice method that is simulated by the keepers to see if any work actually
   * needs to be performed. This method does does not actually need to be
   * executable, and since it is only ever simulated it can consume lots of gas.
   * @dev To ensure that it is never called, you may want to add the
   * cannotExecute modifier from KeeperBase to your implementation of this
   * method.
   * @param checkData specified in the upkeep registration so it is always the
   * same for a registered upkeep. This can easily be broken down into specific
   * arguments using `abi.decode`, so multiple upkeeps can be registered on the
   * same contract and easily differentiated by the contract.
   * @return upkeepNeeded boolean to indicate whether the keeper should call
   * performUpkeep or not.
   * @return performData bytes that the keeper should call performUpkeep with, if
   * upkeep is needed. If you would like to encode data to decode later, try
   * `abi.encode`.
   */
  function checkUpkeep(bytes calldata checkData) external returns (bool upkeepNeeded, bytes memory performData);

  /**
   * @notice method that is actually executed by the keepers, via the registry.
   * The data returned by the checkUpkeep simulation will be passed into
   * this method to actually be executed.
   * @dev The input to this method should not be trusted, and the caller of the
   * method should not even be restricted to any single registry. Anyone should
   * be able call it, and the input should be validated, there is no guarantee
   * that the data passed in is the performData returned from checkUpkeep. This
   * could happen due to malicious keepers, racing keepers, or simply a state
   * change while the performUpkeep transaction is waiting for confirmation.
   * Always validate the data passed in.
   * @param performData is the data which was passed back from the checkData
   * simulation. If it is encoded, it can easily be decoded into other types by
   * calling `abi.decode`. This data should not be trusted, and should be
   * validated against the contract's current state.
   */
  function performUpkeep(bytes calldata performData) external;
}

File 4 of 19 : AccessControl.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (access/AccessControl.sol)

pragma solidity ^0.8.0;

import "./IAccessControl.sol";
import "../utils/Context.sol";
import "../utils/Strings.sol";
import "../utils/introspection/ERC165.sol";

/**
 * @dev Contract module that allows children to implement role-based access
 * control mechanisms. This is a lightweight version that doesn't allow enumerating role
 * members except through off-chain means by accessing the contract event logs. Some
 * applications may benefit from on-chain enumerability, for those cases see
 * {AccessControlEnumerable}.
 *
 * Roles are referred to by their `bytes32` identifier. These should be exposed
 * in the external API and be unique. The best way to achieve this is by
 * using `public constant` hash digests:
 *
 * ```
 * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
 * ```
 *
 * Roles can be used to represent a set of permissions. To restrict access to a
 * function call, use {hasRole}:
 *
 * ```
 * function foo() public {
 *     require(hasRole(MY_ROLE, msg.sender));
 *     ...
 * }
 * ```
 *
 * Roles can be granted and revoked dynamically via the {grantRole} and
 * {revokeRole} functions. Each role has an associated admin role, and only
 * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
 *
 * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
 * that only accounts with this role will be able to grant or revoke other
 * roles. More complex role relationships can be created by using
 * {_setRoleAdmin}.
 *
 * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
 * grant and revoke this role. Extra precautions should be taken to secure
 * accounts that have been granted it.
 */
abstract contract AccessControl is Context, IAccessControl, ERC165 {
    struct RoleData {
        mapping(address => bool) members;
        bytes32 adminRole;
    }

    mapping(bytes32 => RoleData) private _roles;

    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;

    /**
     * @dev Modifier that checks that an account has a specific role. Reverts
     * with a standardized message including the required role.
     *
     * The format of the revert reason is given by the following regular expression:
     *
     *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
     *
     * _Available since v4.1._
     */
    modifier onlyRole(bytes32 role) {
        _checkRole(role);
        _;
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) public view virtual override returns (bool) {
        return _roles[role].members[account];
    }

    /**
     * @dev Revert with a standard message if `_msgSender()` is missing `role`.
     * Overriding this function changes the behavior of the {onlyRole} modifier.
     *
     * Format of the revert message is described in {_checkRole}.
     *
     * _Available since v4.6._
     */
    function _checkRole(bytes32 role) internal view virtual {
        _checkRole(role, _msgSender());
    }

    /**
     * @dev Revert with a standard message if `account` is missing `role`.
     *
     * The format of the revert reason is given by the following regular expression:
     *
     *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
     */
    function _checkRole(bytes32 role, address account) internal view virtual {
        if (!hasRole(role, account)) {
            revert(
                string(
                    abi.encodePacked(
                        "AccessControl: account ",
                        Strings.toHexString(uint160(account), 20),
                        " is missing role ",
                        Strings.toHexString(uint256(role), 32)
                    )
                )
            );
        }
    }

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) {
        return _roles[role].adminRole;
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     *
     * May emit a {RoleGranted} event.
     */
    function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
        _grantRole(role, account);
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     *
     * May emit a {RoleRevoked} event.
     */
    function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
        _revokeRole(role, account);
    }

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been revoked `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `account`.
     *
     * May emit a {RoleRevoked} event.
     */
    function renounceRole(bytes32 role, address account) public virtual override {
        require(account == _msgSender(), "AccessControl: can only renounce roles for self");

        _revokeRole(role, account);
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event. Note that unlike {grantRole}, this function doesn't perform any
     * checks on the calling account.
     *
     * May emit a {RoleGranted} event.
     *
     * [WARNING]
     * ====
     * This function should only be called from the constructor when setting
     * up the initial roles for the system.
     *
     * Using this function in any other way is effectively circumventing the admin
     * system imposed by {AccessControl}.
     * ====
     *
     * NOTE: This function is deprecated in favor of {_grantRole}.
     */
    function _setupRole(bytes32 role, address account) internal virtual {
        _grantRole(role, account);
    }

    /**
     * @dev Sets `adminRole` as ``role``'s admin role.
     *
     * Emits a {RoleAdminChanged} event.
     */
    function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
        bytes32 previousAdminRole = getRoleAdmin(role);
        _roles[role].adminRole = adminRole;
        emit RoleAdminChanged(role, previousAdminRole, adminRole);
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleGranted} event.
     */
    function _grantRole(bytes32 role, address account) internal virtual {
        if (!hasRole(role, account)) {
            _roles[role].members[account] = true;
            emit RoleGranted(role, account, _msgSender());
        }
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleRevoked} event.
     */
    function _revokeRole(bytes32 role, address account) internal virtual {
        if (hasRole(role, account)) {
            _roles[role].members[account] = false;
            emit RoleRevoked(role, account, _msgSender());
        }
    }
}

File 5 of 19 : AccessControlEnumerable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (access/AccessControlEnumerable.sol)

pragma solidity ^0.8.0;

import "./IAccessControlEnumerable.sol";
import "./AccessControl.sol";
import "../utils/structs/EnumerableSet.sol";

/**
 * @dev Extension of {AccessControl} that allows enumerating the members of each role.
 */
abstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessControl {
    using EnumerableSet for EnumerableSet.AddressSet;

    mapping(bytes32 => EnumerableSet.AddressSet) private _roleMembers;

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IAccessControlEnumerable).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @dev Returns one of the accounts that have `role`. `index` must be a
     * value between 0 and {getRoleMemberCount}, non-inclusive.
     *
     * Role bearers are not sorted in any particular way, and their ordering may
     * change at any point.
     *
     * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
     * you perform all queries on the same block. See the following
     * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
     * for more information.
     */
    function getRoleMember(bytes32 role, uint256 index) public view virtual override returns (address) {
        return _roleMembers[role].at(index);
    }

    /**
     * @dev Returns the number of accounts that have `role`. Can be used
     * together with {getRoleMember} to enumerate all bearers of a role.
     */
    function getRoleMemberCount(bytes32 role) public view virtual override returns (uint256) {
        return _roleMembers[role].length();
    }

    /**
     * @dev Overload {_grantRole} to track enumerable memberships
     */
    function _grantRole(bytes32 role, address account) internal virtual override {
        super._grantRole(role, account);
        _roleMembers[role].add(account);
    }

    /**
     * @dev Overload {_revokeRole} to track enumerable memberships
     */
    function _revokeRole(bytes32 role, address account) internal virtual override {
        super._revokeRole(role, account);
        _roleMembers[role].remove(account);
    }
}

File 6 of 19 : IAccessControl.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)

pragma solidity ^0.8.0;

/**
 * @dev External interface of AccessControl declared to support ERC165 detection.
 */
interface IAccessControl {
    /**
     * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
     *
     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
     * {RoleAdminChanged} not being emitted signaling this.
     *
     * _Available since v3.1._
     */
    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);

    /**
     * @dev Emitted when `account` is granted `role`.
     *
     * `sender` is the account that originated the contract call, an admin role
     * bearer except when using {AccessControl-_setupRole}.
     */
    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Emitted when `account` is revoked `role`.
     *
     * `sender` is the account that originated the contract call:
     *   - if using `revokeRole`, it is the admin role bearer
     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
     */
    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) external view returns (bool);

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {AccessControl-_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) external view returns (bytes32);

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function grantRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function revokeRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been granted `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `account`.
     */
    function renounceRole(bytes32 role, address account) external;
}

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

pragma solidity ^0.8.0;

import "./IAccessControl.sol";

/**
 * @dev External interface of AccessControlEnumerable declared to support ERC165 detection.
 */
interface IAccessControlEnumerable is IAccessControl {
    /**
     * @dev Returns one of the accounts that have `role`. `index` must be a
     * value between 0 and {getRoleMemberCount}, non-inclusive.
     *
     * Role bearers are not sorted in any particular way, and their ordering may
     * change at any point.
     *
     * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
     * you perform all queries on the same block. See the following
     * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
     * for more information.
     */
    function getRoleMember(bytes32 role, uint256 index) external view returns (address);

    /**
     * @dev Returns the number of accounts that have `role`. Can be used
     * together with {getRoleMember} to enumerate all bearers of a role.
     */
    function getRoleMemberCount(bytes32 role) external view returns (uint256);
}

File 8 of 19 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

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

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

    /**
     * @dev Returns the 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 `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, 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 `from` to `to` 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 from,
        address to,
        uint256 amount
    ) external returns (bool);
}

File 9 of 19 : 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 10 of 19 : 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);
}

File 11 of 19 : SafeERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";
import "../extensions/draft-IERC20Permit.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));
        }
    }

    function safePermit(
        IERC20Permit token,
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        uint256 nonceBefore = token.nonces(owner);
        token.permit(owner, spender, value, deadline, v, r, s);
        uint256 nonceAfter = token.nonces(owner);
        require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
    }

    /**
     * @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 12 of 19 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @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
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 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
                /// @solidity memory-safe-assembly
                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

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

pragma solidity ^0.8.0;

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

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

File 14 of 19 : Multicall.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (utils/Multicall.sol)

pragma solidity ^0.8.0;

import "./Address.sol";

/**
 * @dev Provides a function to batch together multiple calls in a single external call.
 *
 * _Available since v4.1._
 */
abstract contract Multicall {
    /**
     * @dev Receives and executes a batch of function calls on this contract.
     */
    function multicall(bytes[] calldata data) external virtual returns (bytes[] memory results) {
        results = new bytes[](data.length);
        for (uint256 i = 0; i < data.length; i++) {
            results[i] = Address.functionDelegateCall(address(this), data[i]);
        }
        return results;
    }
}

File 15 of 19 : Strings.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol)

pragma solidity ^0.8.0;

/**
 * @dev String operations.
 */
library Strings {
    bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";
    uint8 private constant _ADDRESS_LENGTH = 20;

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        // Inspired by OraclizeAPI's implementation - MIT licence
        // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol

        if (value == 0) {
            return "0";
        }
        uint256 temp = value;
        uint256 digits;
        while (temp != 0) {
            digits++;
            temp /= 10;
        }
        bytes memory buffer = new bytes(digits);
        while (value != 0) {
            digits -= 1;
            buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
            value /= 10;
        }
        return string(buffer);
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        if (value == 0) {
            return "0x00";
        }
        uint256 temp = value;
        uint256 length = 0;
        while (temp != 0) {
            length++;
            temp >>= 8;
        }
        return toHexString(value, length);
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = _HEX_SYMBOLS[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }

    /**
     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
     */
    function toHexString(address addr) internal pure returns (string memory) {
        return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
    }
}

File 16 of 19 : ERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)

pragma solidity ^0.8.0;

import "./IERC165.sol";

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 *
 * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
 */
abstract contract ERC165 is IERC165 {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}

File 17 of 19 : IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

File 18 of 19 : EnumerableSet.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/structs/EnumerableSet.sol)

pragma solidity ^0.8.0;

/**
 * @dev Library for managing
 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
 * types.
 *
 * Sets have the following properties:
 *
 * - Elements are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Elements are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```
 * contract Example {
 *     // Add the library methods
 *     using EnumerableSet for EnumerableSet.AddressSet;
 *
 *     // Declare a set state variable
 *     EnumerableSet.AddressSet private mySet;
 * }
 * ```
 *
 * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
 * and `uint256` (`UintSet`) are supported.
 *
 * [WARNING]
 * ====
 *  Trying to delete such a structure from storage will likely result in data corruption, rendering the structure unusable.
 *  See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
 *
 *  In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an array of EnumerableSet.
 * ====
 */
library EnumerableSet {
    // To implement this library for multiple types with as little code
    // repetition as possible, we write it in terms of a generic Set type with
    // bytes32 values.
    // The Set implementation uses private functions, and user-facing
    // implementations (such as AddressSet) are just wrappers around the
    // underlying Set.
    // This means that we can only create new EnumerableSets for types that fit
    // in bytes32.

    struct Set {
        // Storage of set values
        bytes32[] _values;
        // Position of the value in the `values` array, plus 1 because index 0
        // means a value is not in the set.
        mapping(bytes32 => uint256) _indexes;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function _add(Set storage set, bytes32 value) private returns (bool) {
        if (!_contains(set, value)) {
            set._values.push(value);
            // The value is stored at length-1, but we add 1 to all indexes
            // and use 0 as a sentinel value
            set._indexes[value] = set._values.length;
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function _remove(Set storage set, bytes32 value) private returns (bool) {
        // We read and store the value's index to prevent multiple reads from the same storage slot
        uint256 valueIndex = set._indexes[value];

        if (valueIndex != 0) {
            // Equivalent to contains(set, value)
            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
            // the array, and then remove the last element (sometimes called as 'swap and pop').
            // This modifies the order of the array, as noted in {at}.

            uint256 toDeleteIndex = valueIndex - 1;
            uint256 lastIndex = set._values.length - 1;

            if (lastIndex != toDeleteIndex) {
                bytes32 lastValue = set._values[lastIndex];

                // Move the last value to the index where the value to delete is
                set._values[toDeleteIndex] = lastValue;
                // Update the index for the moved value
                set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
            }

            // Delete the slot where the moved value was stored
            set._values.pop();

            // Delete the index for the deleted slot
            delete set._indexes[value];

            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function _contains(Set storage set, bytes32 value) private view returns (bool) {
        return set._indexes[value] != 0;
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function _length(Set storage set) private view returns (uint256) {
        return set._values.length;
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function _at(Set storage set, uint256 index) private view returns (bytes32) {
        return set._values[index];
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function _values(Set storage set) private view returns (bytes32[] memory) {
        return set._values;
    }

    // Bytes32Set

    struct Bytes32Set {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _add(set._inner, value);
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _remove(set._inner, value);
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
        return _contains(set._inner, value);
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(Bytes32Set storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
        return _at(set._inner, index);
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
        return _values(set._inner);
    }

    // AddressSet

    struct AddressSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(AddressSet storage set, address value) internal returns (bool) {
        return _add(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(AddressSet storage set, address value) internal returns (bool) {
        return _remove(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(AddressSet storage set, address value) internal view returns (bool) {
        return _contains(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(AddressSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(AddressSet storage set, uint256 index) internal view returns (address) {
        return address(uint160(uint256(_at(set._inner, index))));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(AddressSet storage set) internal view returns (address[] memory) {
        bytes32[] memory store = _values(set._inner);
        address[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }

    // UintSet

    struct UintSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(UintSet storage set, uint256 value) internal returns (bool) {
        return _add(set._inner, bytes32(value));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(UintSet storage set, uint256 value) internal returns (bool) {
        return _remove(set._inner, bytes32(value));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(UintSet storage set, uint256 value) internal view returns (bool) {
        return _contains(set._inner, bytes32(value));
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function length(UintSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(UintSet storage set, uint256 index) internal view returns (uint256) {
        return uint256(_at(set._inner, index));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(UintSet storage set) internal view returns (uint256[] memory) {
        bytes32[] memory store = _values(set._inner);
        uint256[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }
}

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

pragma solidity 0.8.16;

import {AccessControlEnumerable} from "@openzeppelin/contracts/access/AccessControlEnumerable.sol";
import {IERC20Metadata, IERC20} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {Multicall} from "@openzeppelin/contracts/utils/Multicall.sol";

import {KeeperCompatibleInterface} from "@chainlink/contracts/src/v0.8/KeeperCompatible.sol";

interface IGame {
    function hasPendingBets(address token) external view returns (bool);

    function withdrawTokensVRFFees(address token) external;
}

// import "hardhat/console.sol";

/// @title BetSwirl's Bank
/// @author Romuald Hog
/// @notice The Bank contract holds the casino's funds,
/// whitelist the games betting tokens,
/// define the max bet amount based on a risk,
/// payout the bet profit to user and collect the loss bet amount from the game's contract,
/// split and allocate the house edge taken from each bet (won or loss).
/// The admin role is transfered to a Timelock that execute administrative tasks,
/// only the Games could payout the bet profit from the bank, and send the loss bet amount to the bank.
/// @dev All rates are in basis point.
contract Bank is AccessControlEnumerable, KeeperCompatibleInterface, Multicall {
    using SafeERC20 for IERC20;

    /// @notice Enum to identify the Chainlink Upkeep registration.
    enum UpkeepActions {
        DistributePartnerHouseEdge
    }

    /// @notice Token's house edge allocations struct.
    /// The games house edge is split into several allocations.
    /// The allocated amounts stays in the bank until authorized parties withdraw. They are subtracted from the balance.
    /// @param bank Rate to be allocated to the bank, on bet payout.
    /// @param dividend Rate to be allocated as staking rewards, on bet payout.
    /// @param partner Rate to be allocated to the partner, on bet payout.
    /// @param treasury Rate to be allocated to the treasury, on bet payout.
    /// @param team Rate to be allocated to the team, on bet payout.
    /// @param dividendAmount The number of tokens to be sent as staking rewards.
    /// @param partnerAmount The number of tokens to be sent to the partner.
    /// @param treasuryAmount The number of tokens to be sent to the treasury.
    /// @param teamAmount The number of tokens to be sent to the team.
    struct HouseEdgeSplit {
        uint16 bank;
        uint16 dividend;
        uint16 partner;
        uint16 treasury;
        uint16 team;
        uint256 dividendAmount;
        uint256 partnerAmount;
        uint256 treasuryAmount;
        uint256 teamAmount;
    }

    /// @notice Token struct.
    /// List of tokens to bet on games.
    /// @param allowed Whether the token is allowed for bets.
    /// @param paused Whether the token is paused for bets.
    /// @param balanceRisk Defines the maximum bank payout, used to calculate the max bet amount.
    /// @param VRFSubId Chainlink VRF v2 subscription ID.
    /// @param partner Address of the partner to manage the token and receive the house edge.
    /// @param minBetAmount Minimum bet amount.
    /// @param minPartnerTransferAmount The minimum amount of token to distribute the partner house edge.
    /// @param houseEdgeSplit House edge allocations.
    struct Token {
        bool allowed;
        bool paused;
        uint16 balanceRisk;
        uint64 VRFSubId;
        address partner;
        uint256 minBetAmount;
        uint256 minPartnerTransferAmount;
        HouseEdgeSplit houseEdgeSplit;
    }

    /// @notice Token's metadata struct. It contains additional information from the ERC20 token.
    /// @dev Only used on the `getTokens` getter for the front-end.
    /// @param decimals Number of token's decimals.
    /// @param tokenAddress Contract address of the token.
    /// @param name Name of the token.
    /// @param symbol Symbol of the token.
    /// @param token Token data.
    struct TokenMetadata {
        uint8 decimals;
        address tokenAddress;
        string name;
        string symbol;
        Token token;
    }

    /// @notice Number of tokens added.
    uint8 private _tokensCount;

    /// @notice Treasury multi-sig wallet.
    address public immutable treasury;

    /// @notice Team wallet.
    address public teamWallet;

    /// @notice Role associated to Games smart contracts.
    bytes32 public constant GAME_ROLE = keccak256("GAME_ROLE");

    /// @notice Role associated to SwirlMaster smart contract.
    bytes32 public constant SWIRLMASTER_ROLE = keccak256("SWIRLMASTER_ROLE");

    /// @notice Maps tokens addresses to token configuration.
    mapping(address => Token) public tokens;

    /// @notice Maps tokens indexes to token address.
    mapping(uint8 => address) private _tokensList;

    /// @notice Emitted after the team wallet is set.
    /// @param teamWallet The team wallet address.
    event SetTeamWallet(address teamWallet);

    /// @notice Emitted after a token is added.
    /// @param token Address of the token.
    event AddToken(address token);

    /// @notice Emitted after the balance risk is set.
    /// @param balanceRisk Rate defining the balance risk.
    event SetBalanceRisk(address indexed token, uint16 balanceRisk);

    /// @notice Emitted after a token is allowed.
    /// @param token Address of the token.
    /// @param allowed Whether the token is allowed for betting.
    event SetAllowedToken(address indexed token, bool allowed);

    /// @notice Emitted after the minimum bet amount is set for a token.
    /// @param token Address of the token.
    /// @param minBetAmount Minimum bet amount.
    event SetTokenMinBetAmount(address indexed token, uint256 minBetAmount);

    /// @notice Emitted after the token's VRF subscription ID is set.
    /// @param token Address of the token.
    /// @param subId Subscription ID.
    event SetTokenVRFSubId(address indexed token, uint64 subId);

    /// @notice Emitted after a token is paused.
    /// @param token Address of the token.
    /// @param paused Whether the token is paused for betting.
    event SetPausedToken(address indexed token, bool paused);

    /// @notice Emitted after the Upkeep minimum transfer amount is set.
    /// @param token Address of the token.
    /// @param minPartnerTransferAmount Minimum amount of token to allow transfer.
    event SetMinPartnerTransferAmount(
        address indexed token,
        uint256 minPartnerTransferAmount
    );

    /// @notice Emitted after a token partner is set.
    /// @param token Address of the token.
    /// @param partner Address of the partner.
    event SetTokenPartner(address indexed token, address partner);

    /// @notice Emitted after a token deposit.
    /// @param token Address of the token.
    /// @param amount The number of token deposited.
    event Deposit(address indexed token, uint256 amount);

    /// @notice Emitted after a token withdrawal.
    /// @param token Address of the token.
    /// @param amount The number of token withdrawn.
    event Withdraw(address indexed token, uint256 amount);

    /// @notice Emitted after the token's house edge allocations for bet payout is set.
    /// @param token Address of the token.
    /// @param bank Rate to be allocated to the bank, on bet payout.
    /// @param dividend Rate to be allocated as staking rewards, on bet payout.
    /// @param partner Rate to be allocated to the partner, on bet payout.
    /// @param treasury Rate to be allocated to the treasury, on bet payout.
    /// @param team Rate to be allocated to the team, on bet payout.
    event SetTokenHouseEdgeSplit(
        address indexed token,
        uint16 bank,
        uint16 dividend,
        uint16 partner,
        uint16 treasury,
        uint16 team
    );

    /// @notice Emitted after the token's treasury and team allocations are distributed.
    /// @param token Address of the token.
    /// @param treasuryAmount The number of tokens sent to the treasury.
    /// @param teamAmount The number of tokens sent to the team.
    event HouseEdgeDistribution(
        address indexed token,
        uint256 treasuryAmount,
        uint256 teamAmount
    );
    /// @notice Emitted after the token's partner allocation is distributed.
    /// @param token Address of the token.
    /// @param partnerAmount The number of tokens sent to the partner.
    event HouseEdgePartnerDistribution(
        address indexed token,
        uint256 partnerAmount
    );

    /// @notice Emitted after the token's dividend allocation is distributed.
    /// @param token Address of the token.
    /// @param amount The number of tokens sent to the SwirlMaster.
    event HarvestDividend(address indexed token, uint256 amount);

    /// @notice Emitted after the token's house edge is allocated.
    /// @param token Address of the token.
    /// @param bank The number of tokens allocated to bank.
    /// @param dividend The number of tokens allocated as staking rewards.
    /// @param partner The number of tokens allocated to the partner.
    /// @param treasury The number of tokens allocated to the treasury.
    /// @param team The number of tokens allocated to the team.
    event AllocateHouseEdgeAmount(
        address indexed token,
        uint256 bank,
        uint256 dividend,
        uint256 partner,
        uint256 treasury,
        uint256 team
    );

    /// @notice Emitted after the game's fees are sent to the bank.
    /// @param token Address of the token.
    /// @param newBalance New token balance.
    /// @param fees Fees received from game.
    event AccountFees(address indexed token, uint256 newBalance, uint256 fees);

    /// @notice Emitted after the bet profit amount is sent to the user.
    /// @param token Address of the token.
    /// @param newBalance New token balance.
    /// @param profit Bet profit amount sent.
    event Payout(address indexed token, uint256 newBalance, uint256 profit);

    /// @notice Emitted after the bet amount is collected from the game smart contract.
    /// @param token Address of the token.
    /// @param newBalance New token balance.
    /// @param amount Bet amount collected.
    event CashIn(address indexed token, uint256 newBalance, uint256 amount);

    /// @notice Reverting error when trying to add an existing token.
    error TokenExists();
    /// @notice Reverting error when setting the house edge allocations, but the sum isn't 100%.
    /// @param splitSum Sum of the house edge allocations rates.
    error WrongHouseEdgeSplit(uint16 splitSum);
    /// @notice Reverting error when sender isn't allowed.
    error AccessDenied();
    /// @notice Reverting error when team wallet or treasury is the zero address.
    error WrongAddress();
    /// @notice Reverting error when withdrawing a non paused token.
    error TokenNotPaused();
    /// @notice Reverting error when token has pending bets on a game.
    error TokenHasPendingBets();

    /// @notice Modifier that checks that an account is allowed to interact with a token.
    /// @param role The required role.
    /// @param token The token address.
    modifier onlyTokenOwner(bytes32 role, address token) {
        address partner = tokens[token].partner;
        if (partner == address(0)) {
            _checkRole(role, msg.sender);
        } else if (msg.sender != partner) {
            revert AccessDenied();
        }
        _;
    }

    /// @notice Initialize the contract's admin role to the deployer, and state variables.
    /// @param treasuryAddress Treasury multi-sig wallet.
    /// @param teamWalletAddress Team wallet.
    constructor(address treasuryAddress, address teamWalletAddress) {
        if (treasuryAddress == address(0)) {
            revert WrongAddress();
        }

        treasury = treasuryAddress;

        // The ownership should then be transfered to a multi-sig.
        _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);

        setTeamWallet(teamWalletAddress);
    }

    /// @notice Transfers a specific amount of token to an address.
    /// Uses native transfer or ERC20 transfer depending on the token.
    /// @dev The 0x address is considered the gas token.
    /// @param user Address of destination.
    /// @param token Address of the token.
    /// @param amount Number of tokens.
    function _safeTransfer(
        address user,
        address token,
        uint256 amount
    ) private {
        if (_isGasToken(token)) {
            payable(user).transfer(amount);
        } else {
            IERC20(token).safeTransfer(user, amount);
        }
    }

    /// @notice Check if the token has the 0x address.
    /// @param token Address of the token.
    /// @return Whether the token's address is the 0x address.
    function _isGasToken(address token) private pure returns (bool) {
        return token == address(0);
    }

    /// @notice Deposit funds in the bank to allow gamers to win more.
    /// ERC20 token allowance should be given prior to deposit.
    /// @param token Address of the token.
    /// @param amount Number of tokens.
    function deposit(address token, uint256 amount)
        external
        payable
        onlyTokenOwner(DEFAULT_ADMIN_ROLE, token)
    {
        if (_isGasToken(token)) {
            amount = msg.value;
        } else {
            IERC20(token).safeTransferFrom(msg.sender, address(this), amount);
        }
        emit Deposit(token, amount);
    }

    /// @notice Withdraw funds from the bank. Token has to be paused and no pending bet resolution on games.
    /// @param token Address of the token.
    /// @param amount Number of tokens.
    function withdraw(address token, uint256 amount)
        public
        onlyTokenOwner(DEFAULT_ADMIN_ROLE, token)
    {
        uint256 balance = getBalance(token);
        if (balance != 0) {
            if (!tokens[token].paused) {
                revert TokenNotPaused();
            }

            uint256 roleMemberCount = getRoleMemberCount(GAME_ROLE);
            for (uint256 i; i < roleMemberCount; i++) {
                if (IGame(getRoleMember(GAME_ROLE, i)).hasPendingBets(token)) {
                    revert TokenHasPendingBets();
                }
            }
        }

        if (amount > balance) {
            amount = balance;
        }
        _safeTransfer(msg.sender, token, amount);
        emit Withdraw(token, amount);
    }

    /// @notice Sets the new token balance risk.
    /// @param token Address of the token.
    /// @param balanceRisk Risk rate.
    function setBalanceRisk(address token, uint16 balanceRisk)
        external
        onlyTokenOwner(DEFAULT_ADMIN_ROLE, token)
    {
        tokens[token].balanceRisk = balanceRisk;
        emit SetBalanceRisk(token, balanceRisk);
    }

    /// @notice Adds a new token that'll be enabled for the games' betting.
    /// Token shouldn't exist yet.
    /// @param token Address of the token.
    function addToken(address token) external onlyRole(DEFAULT_ADMIN_ROLE) {
        if (_tokensCount != 0) {
            for (uint8 i; i < _tokensCount; i++) {
                if (_tokensList[i] == token) {
                    revert TokenExists();
                }
            }
        }
        _tokensList[_tokensCount] = token;
        _tokensCount += 1;
        emit AddToken(token);
    }

    /// @notice Changes the token's bet permission.
    /// @param token Address of the token.
    /// @param allowed Whether the token is enabled for bets.
    function setAllowedToken(address token, bool allowed)
        external
        onlyRole(DEFAULT_ADMIN_ROLE)
    {
        tokens[token].allowed = allowed;
        emit SetAllowedToken(token, allowed);
    }

    /// @notice Changes the token's paused status.
    /// @param token Address of the token.
    /// @param paused Whether the token is paused.
    function setPausedToken(address token, bool paused)
        external
        onlyTokenOwner(DEFAULT_ADMIN_ROLE, token)
    {
        tokens[token].paused = paused;
        emit SetPausedToken(token, paused);
    }

    /// @notice Changes the token's Upkeep min transfer amount.
    /// @param token Address of the token.
    /// @param minPartnerTransferAmount Minimum amount of token to allow transfer.
    function setMinPartnerTransferAmount(
        address token,
        uint256 minPartnerTransferAmount
    ) external onlyTokenOwner(DEFAULT_ADMIN_ROLE, token) {
        tokens[token].minPartnerTransferAmount = minPartnerTransferAmount;
        emit SetMinPartnerTransferAmount(token, minPartnerTransferAmount);
    }

    /// @notice Changes the token's partner address.
    /// It withdraw the available balance, the partner allocation, and the games' VRF fees.
    /// @param token Address of the token.
    /// @param partner Address of the partner.
    function setTokenPartner(address token, address partner)
        external
        onlyTokenOwner(DEFAULT_ADMIN_ROLE, token)
    {
        uint256 roleMemberCount = getRoleMemberCount(GAME_ROLE);
        for (uint256 i; i < roleMemberCount; i++) {
            IGame(getRoleMember(GAME_ROLE, i)).withdrawTokensVRFFees(token);
        }
        withdrawPartnerAmount(token);
        withdraw(token, getBalance(token));
        tokens[token].partner = partner;
        emit SetTokenPartner(token, partner);
    }

    /// @notice Sets the token's house edge allocations for bet payout.
    /// @param token Address of the token.
    /// @param bank Rate to be allocated to the bank, on bet payout.
    /// @param dividend Rate to be allocated as staking rewards, on bet payout.
    /// @param partner Rate to be allocated to the partner, on bet payout.
    /// @param _treasury Rate to be allocated to the treasury, on bet payout.
    /// @param team Rate to be allocated to the team, on bet payout.
    /// @dev `bank`, `dividend`, `_treasury` and `team` rates sum must equals 10000.
    function setHouseEdgeSplit(
        address token,
        uint16 bank,
        uint16 dividend,
        uint16 partner,
        uint16 _treasury,
        uint16 team
    ) external onlyRole(DEFAULT_ADMIN_ROLE) {
        uint16 splitSum = bank + dividend + team + partner + _treasury;
        if (splitSum != 10000) {
            revert WrongHouseEdgeSplit(splitSum);
        }

        HouseEdgeSplit storage tokenHouseEdge = tokens[token].houseEdgeSplit;
        tokenHouseEdge.bank = bank;
        tokenHouseEdge.dividend = dividend;
        tokenHouseEdge.partner = partner;
        tokenHouseEdge.treasury = _treasury;
        tokenHouseEdge.team = team;

        emit SetTokenHouseEdgeSplit(
            token,
            bank,
            dividend,
            partner,
            _treasury,
            team
        );
    }

    /// @notice Sets the minimum bet amount for a specific token.
    /// @param token Address of the token.
    /// @param tokenMinBetAmount Minimum bet amount.
    function setTokenMinBetAmount(address token, uint256 tokenMinBetAmount)
        external
        onlyTokenOwner(DEFAULT_ADMIN_ROLE, token)
    {
        tokens[token].minBetAmount = tokenMinBetAmount;
        emit SetTokenMinBetAmount(token, tokenMinBetAmount);
    }

    /// @notice Sets the Chainlink VRF subscription ID for a specific token.
    /// @param token Address of the token.
    /// @param subId Subscription ID.
    function setTokenVRFSubId(address token, uint64 subId)
        external
        onlyTokenOwner(DEFAULT_ADMIN_ROLE, token)
    {
        tokens[token].VRFSubId = subId;
        emit SetTokenVRFSubId(token, subId);
    }

    /// @notice Harvests tokens dividends.
    /// @return The list of tokens addresses.
    /// @return The list of tokens' amounts harvested.
    function harvestDividends()
        external
        onlyRole(SWIRLMASTER_ROLE)
        returns (address[] memory, uint256[] memory)
    {
        address[] memory _tokens = new address[](_tokensCount);
        uint256[] memory _amounts = new uint256[](_tokensCount);

        for (uint8 i; i < _tokensCount; i++) {
            address tokenAddress = _tokensList[i];
            Token storage token = tokens[tokenAddress];
            uint256 dividendAmount = token.houseEdgeSplit.dividendAmount;
            if (dividendAmount != 0) {
                delete token.houseEdgeSplit.dividendAmount;
                _safeTransfer(msg.sender, tokenAddress, dividendAmount);
                emit HarvestDividend(tokenAddress, dividendAmount);
                _tokens[i] = tokenAddress;
                _amounts[i] = dividendAmount;
            }
        }

        return (_tokens, _amounts);
    }

    /// @notice Get the available tokens dividends amounts.
    /// @return The list of tokens addresses.
    /// @return The list of tokens' amounts harvested.
    function getDividends()
        external
        view
        returns (address[] memory, uint256[] memory)
    {
        address[] memory _tokens = new address[](_tokensCount);
        uint256[] memory _amounts = new uint256[](_tokensCount);

        for (uint8 i; i < _tokensCount; i++) {
            address tokenAddress = _tokensList[i];
            Token storage token = tokens[tokenAddress];
            uint256 dividendAmount = token.houseEdgeSplit.dividendAmount;
            if (dividendAmount != 0) {
                _tokens[i] = tokenAddress;
                _amounts[i] = dividendAmount;
            }
        }

        return (_tokens, _amounts);
    }

    /// @notice Splits the house edge fees and allocates them as dividends, to the partner, the bank, the treasury, and team.
    /// @param token Address of the token.
    /// @param fees Bet amount and bet profit fees amount.
    function _allocateHouseEdge(address token, uint256 fees) private {
        HouseEdgeSplit storage tokenHouseEdge = tokens[token].houseEdgeSplit;

        uint256 partnerAmount;
        if (tokenHouseEdge.partner != 0) {
            partnerAmount = ((fees * tokenHouseEdge.partner) / 10000);
            tokenHouseEdge.partnerAmount += partnerAmount;
        }

        uint256 dividendAmount = (fees * tokenHouseEdge.dividend) / 10000;
        tokenHouseEdge.dividendAmount += dividendAmount;

        // The bank also get allocated a share of the house edge.
        uint256 bankAmount = (fees * tokenHouseEdge.bank) / 10000;

        uint256 treasuryAmount = (fees * tokenHouseEdge.treasury) / 10000;
        tokenHouseEdge.treasuryAmount += treasuryAmount;

        uint256 teamAmount = (fees * tokenHouseEdge.team) / 10000;
        tokenHouseEdge.teamAmount += teamAmount;

        emit AllocateHouseEdgeAmount(
            token,
            bankAmount,
            dividendAmount,
            partnerAmount,
            treasuryAmount,
            teamAmount
        );
    }

    /// @notice Receive game's fees.
    /// @param token Address of the token.
    /// @param fees Bet amount and bet profit fees amount.
    function accountFees(address token, uint256 fees)
        external
        payable
        onlyRole(GAME_ROLE)
    {
        _allocateHouseEdge(token, fees);

        emit AccountFees(
            token,
            getBalance(token),
            _isGasToken(token) ? msg.value : fees
        );
    }

    /// @notice Payouts a winning bet, and allocate the house edge fee.
    /// @param user Address of the gamer.
    /// @param token Address of the token.
    /// @param profit Number of tokens to be sent to the gamer.
    /// @param fees Bet amount and bet profit fees amount.
    function payout(
        address payable user,
        address token,
        uint256 profit,
        uint256 fees
    ) external payable onlyRole(GAME_ROLE) {
        _allocateHouseEdge(token, fees);

        // Pay the user
        _safeTransfer(user, token, profit);
        emit Payout(token, getBalance(token), profit);
    }

    /// @notice Accounts a loss bet.
    /// @dev In case of an ERC20, the bet amount should be transfered prior to this tx.
    /// @dev In case of the gas token, the bet amount is sent along with this tx.
    /// @param tokenAddress Address of the token.
    /// @param amount Loss bet amount.
    function cashIn(address tokenAddress, uint256 amount)
        external
        payable
        onlyRole(GAME_ROLE)
    {
        emit CashIn(
            tokenAddress,
            getBalance(tokenAddress),
            _isGasToken(tokenAddress) ? msg.value : amount
        );
    }

    /// @notice Executed by Chainlink Keepers when `upkeepNeeded` is true.
    /// @param performData Data which was passed back from `checkUpkeep`.
    function performUpkeep(bytes calldata performData) external override {
        (UpkeepActions upkeepAction, address tokenAddress) = abi.decode(
            performData,
            (UpkeepActions, address)
        );
        Token memory token = tokens[tokenAddress];

        if (
            upkeepAction == UpkeepActions.DistributePartnerHouseEdge &&
            token.houseEdgeSplit.partnerAmount > token.minPartnerTransferAmount
        ) {
            withdrawPartnerAmount(tokenAddress);
        }
    }

    /// @dev For the front-end
    function getTokens() external view returns (TokenMetadata[] memory) {
        TokenMetadata[] memory _tokens = new TokenMetadata[](_tokensCount);
        for (uint8 i; i < _tokensCount; i++) {
            address tokenAddress = _tokensList[i];
            Token memory token = tokens[tokenAddress];
            if (_isGasToken(tokenAddress)) {
                _tokens[i] = TokenMetadata({
                    decimals: 18,
                    tokenAddress: tokenAddress,
                    name: "ETH",
                    symbol: "ETH",
                    token: token
                });
            } else {
                IERC20Metadata erc20Metadata = IERC20Metadata(tokenAddress);
                _tokens[i] = TokenMetadata({
                    decimals: erc20Metadata.decimals(),
                    tokenAddress: tokenAddress,
                    name: erc20Metadata.name(),
                    symbol: erc20Metadata.symbol(),
                    token: token
                });
            }
        }
        return _tokens;
    }

    /// @notice Gets the token's min bet amount.
    /// @param token Address of the token.
    /// @return minBetAmount Min bet amount.
    /// @dev The min bet amount should be at least 10000 cause of the `getMaxBetAmount` calculation.
    function getMinBetAmount(address token)
        external
        view
        returns (uint256 minBetAmount)
    {
        minBetAmount = tokens[token].minBetAmount;
        if (minBetAmount == 0) {
            minBetAmount = 10000;
        }
    }

    /// @notice Calculates the max bet amount based on the token balance, the balance risk, and the game multiplier.
    /// @param token Address of the token.
    /// @param multiplier The bet amount leverage determines the user's profit amount. 10000 = 100% = no profit.
    /// @return Maximum bet amount for the token.
    /// @dev The multiplier should be at least 10000.
    function getMaxBetAmount(address token, uint256 multiplier)
        external
        view
        returns (uint256)
    {
        return (getBalance(token) * tokens[token].balanceRisk) / multiplier;
    }

    /// @notice Gets the token's allow status used on the games smart contracts.
    /// @param tokenAddress Address of the token.
    /// @return Whether the token is enabled for bets.
    function isAllowedToken(address tokenAddress) external view returns (bool) {
        Token memory token = tokens[tokenAddress];
        return token.allowed && !token.paused;
    }

    /// @notice Runs by Chainlink Keepers at every block to determine if `performUpkeep` should be called.
    /// @param checkData Fixed and specified at Upkeep registration.
    /// @return upkeepNeeded Boolean that when True will trigger the on-chain performUpkeep call.
    /// @return performData Bytes that will be used as input parameter when calling performUpkeep.
    /// @dev `checkData` and `performData` are encoded with types (uint8, address).
    function checkUpkeep(bytes calldata checkData)
        external
        view
        override
        returns (bool upkeepNeeded, bytes memory performData)
    {
        (UpkeepActions upkeepAction, address tokenAddressData) = abi.decode(
            checkData,
            (UpkeepActions, address)
        );
        if (upkeepAction == UpkeepActions.DistributePartnerHouseEdge) {
            Token memory token = tokens[tokenAddressData];
            if (
                token.houseEdgeSplit.partnerAmount >
                token.minPartnerTransferAmount
            ) {
                upkeepNeeded = true;
                performData = abi.encode(upkeepAction, tokenAddressData);
            }
        }
    }

    /// @notice Gets the token's Chainlink VRF v2 Subscription ID.
    /// @param token Address of the token.
    /// @return Chainlink VRF v2 Subscription ID.
    function getVRFSubId(address token) external view returns (uint64) {
        return tokens[token].VRFSubId;
    }

    /// @notice Gets the token's owner.
    /// @param token Address of the token.
    /// @return Address of the owner.
    function getTokenOwner(address token) external view returns (address) {
        address partner = tokens[token].partner;
        if (partner == address(0)) {
            return getRoleMember(DEFAULT_ADMIN_ROLE, 0);
        } else {
            return partner;
        }
    }

    /// @notice Sets the new team wallet.
    /// @param _teamWallet The team wallet address.
    function setTeamWallet(address _teamWallet)
        public
        onlyRole(DEFAULT_ADMIN_ROLE)
    {
        if (_teamWallet == address(0)) {
            revert WrongAddress();
        }
        teamWallet = _teamWallet;
        emit SetTeamWallet(teamWallet);
    }

    /// @notice Distributes the token's treasury and team allocations amounts.
    /// @param tokenAddress Address of the token.
    function withdrawHouseEdgeAmount(address tokenAddress) public {
        HouseEdgeSplit storage tokenHouseEdge = tokens[tokenAddress]
            .houseEdgeSplit;
        uint256 treasuryAmount = tokenHouseEdge.treasuryAmount;
        uint256 teamAmount = tokenHouseEdge.teamAmount;
        if (treasuryAmount != 0) {
            delete tokenHouseEdge.treasuryAmount;
            _safeTransfer(treasury, tokenAddress, treasuryAmount);
        }
        if (teamAmount != 0) {
            delete tokenHouseEdge.teamAmount;
            _safeTransfer(teamWallet, tokenAddress, teamAmount);
        }
        if (treasuryAmount != 0 || teamAmount != 0) {
            emit HouseEdgeDistribution(
                tokenAddress,
                treasuryAmount,
                teamAmount
            );
        }
    }

    /// @notice Distributes the token's partner amount.
    /// @param tokenAddress Address of the token.
    function withdrawPartnerAmount(address tokenAddress) public {
        Token storage token = tokens[tokenAddress];
        uint256 partnerAmount = token.houseEdgeSplit.partnerAmount;
        if (partnerAmount != 0 && token.partner != address(0)) {
            delete token.houseEdgeSplit.partnerAmount;
            _safeTransfer(token.partner, tokenAddress, partnerAmount);
            emit HouseEdgePartnerDistribution(tokenAddress, partnerAmount);
        }
    }

    /// @notice Gets the token's balance.
    /// The token's house edge allocation amounts are subtracted from the balance.
    /// @param token Address of the token.
    /// @return The amount of token available for profits.
    function getBalance(address token) public view returns (uint256) {
        uint256 balance;
        if (_isGasToken(token)) {
            balance = address(this).balance;
        } else {
            balance = IERC20(token).balanceOf(address(this));
        }
        HouseEdgeSplit memory tokenHouseEdgeSplit = tokens[token]
            .houseEdgeSplit;
        return
            balance -
            tokenHouseEdgeSplit.dividendAmount -
            tokenHouseEdgeSplit.partnerAmount -
            tokenHouseEdgeSplit.treasuryAmount -
            tokenHouseEdgeSplit.teamAmount;
    }
}

Settings
{
  "evmVersion": "london",
  "libraries": {},
  "metadata": {
    "bytecodeHash": "ipfs",
    "useLiteralContent": true
  },
  "optimizer": {
    "enabled": true,
    "runs": 8000
  },
  "remappings": [],
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  }
}

Contract ABI

[{"inputs":[{"internalType":"address","name":"treasuryAddress","type":"address"},{"internalType":"address","name":"teamWalletAddress","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AccessDenied","type":"error"},{"inputs":[],"name":"TokenExists","type":"error"},{"inputs":[],"name":"TokenHasPendingBets","type":"error"},{"inputs":[],"name":"TokenNotPaused","type":"error"},{"inputs":[],"name":"WrongAddress","type":"error"},{"inputs":[{"internalType":"uint16","name":"splitSum","type":"uint16"}],"name":"WrongHouseEdgeSplit","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"newBalance","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fees","type":"uint256"}],"name":"AccountFees","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"}],"name":"AddToken","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"bank","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"dividend","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"partner","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"treasury","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"team","type":"uint256"}],"name":"AllocateHouseEdgeAmount","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"newBalance","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"CashIn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"HarvestDividend","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"treasuryAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"teamAmount","type":"uint256"}],"name":"HouseEdgeDistribution","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"partnerAmount","type":"uint256"}],"name":"HouseEdgePartnerDistribution","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"newBalance","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"profit","type":"uint256"}],"name":"Payout","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"bool","name":"allowed","type":"bool"}],"name":"SetAllowedToken","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint16","name":"balanceRisk","type":"uint16"}],"name":"SetBalanceRisk","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"minPartnerTransferAmount","type":"uint256"}],"name":"SetMinPartnerTransferAmount","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"bool","name":"paused","type":"bool"}],"name":"SetPausedToken","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"teamWallet","type":"address"}],"name":"SetTeamWallet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint16","name":"bank","type":"uint16"},{"indexed":false,"internalType":"uint16","name":"dividend","type":"uint16"},{"indexed":false,"internalType":"uint16","name":"partner","type":"uint16"},{"indexed":false,"internalType":"uint16","name":"treasury","type":"uint16"},{"indexed":false,"internalType":"uint16","name":"team","type":"uint16"}],"name":"SetTokenHouseEdgeSplit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"minBetAmount","type":"uint256"}],"name":"SetTokenMinBetAmount","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"address","name":"partner","type":"address"}],"name":"SetTokenPartner","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint64","name":"subId","type":"uint64"}],"name":"SetTokenVRFSubId","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Withdraw","type":"event"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"GAME_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SWIRLMASTER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"fees","type":"uint256"}],"name":"accountFees","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"addToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"cashIn","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes","name":"checkData","type":"bytes"}],"name":"checkUpkeep","outputs":[{"internalType":"bool","name":"upkeepNeeded","type":"bool"},{"internalType":"bytes","name":"performData","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"deposit","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"getBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getDividends","outputs":[{"internalType":"address[]","name":"","type":"address[]"},{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"multiplier","type":"uint256"}],"name":"getMaxBetAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"getMinBetAmount","outputs":[{"internalType":"uint256","name":"minBetAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getRoleMember","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleMemberCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"getTokenOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTokens","outputs":[{"components":[{"internalType":"uint8","name":"decimals","type":"uint8"},{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"},{"components":[{"internalType":"bool","name":"allowed","type":"bool"},{"internalType":"bool","name":"paused","type":"bool"},{"internalType":"uint16","name":"balanceRisk","type":"uint16"},{"internalType":"uint64","name":"VRFSubId","type":"uint64"},{"internalType":"address","name":"partner","type":"address"},{"internalType":"uint256","name":"minBetAmount","type":"uint256"},{"internalType":"uint256","name":"minPartnerTransferAmount","type":"uint256"},{"components":[{"internalType":"uint16","name":"bank","type":"uint16"},{"internalType":"uint16","name":"dividend","type":"uint16"},{"internalType":"uint16","name":"partner","type":"uint16"},{"internalType":"uint16","name":"treasury","type":"uint16"},{"internalType":"uint16","name":"team","type":"uint16"},{"internalType":"uint256","name":"dividendAmount","type":"uint256"},{"internalType":"uint256","name":"partnerAmount","type":"uint256"},{"internalType":"uint256","name":"treasuryAmount","type":"uint256"},{"internalType":"uint256","name":"teamAmount","type":"uint256"}],"internalType":"struct Bank.HouseEdgeSplit","name":"houseEdgeSplit","type":"tuple"}],"internalType":"struct Bank.Token","name":"token","type":"tuple"}],"internalType":"struct Bank.TokenMetadata[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"getVRFSubId","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"harvestDividends","outputs":[{"internalType":"address[]","name":"","type":"address[]"},{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"}],"name":"isAllowedToken","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes[]","name":"data","type":"bytes[]"}],"name":"multicall","outputs":[{"internalType":"bytes[]","name":"results","type":"bytes[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address payable","name":"user","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"profit","type":"uint256"},{"internalType":"uint256","name":"fees","type":"uint256"}],"name":"payout","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes","name":"performData","type":"bytes"}],"name":"performUpkeep","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"bool","name":"allowed","type":"bool"}],"name":"setAllowedToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint16","name":"balanceRisk","type":"uint16"}],"name":"setBalanceRisk","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint16","name":"bank","type":"uint16"},{"internalType":"uint16","name":"dividend","type":"uint16"},{"internalType":"uint16","name":"partner","type":"uint16"},{"internalType":"uint16","name":"_treasury","type":"uint16"},{"internalType":"uint16","name":"team","type":"uint16"}],"name":"setHouseEdgeSplit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"minPartnerTransferAmount","type":"uint256"}],"name":"setMinPartnerTransferAmount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"bool","name":"paused","type":"bool"}],"name":"setPausedToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_teamWallet","type":"address"}],"name":"setTeamWallet","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"tokenMinBetAmount","type":"uint256"}],"name":"setTokenMinBetAmount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"partner","type":"address"}],"name":"setTokenPartner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint64","name":"subId","type":"uint64"}],"name":"setTokenVRFSubId","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"teamWallet","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"tokens","outputs":[{"internalType":"bool","name":"allowed","type":"bool"},{"internalType":"bool","name":"paused","type":"bool"},{"internalType":"uint16","name":"balanceRisk","type":"uint16"},{"internalType":"uint64","name":"VRFSubId","type":"uint64"},{"internalType":"address","name":"partner","type":"address"},{"internalType":"uint256","name":"minBetAmount","type":"uint256"},{"internalType":"uint256","name":"minPartnerTransferAmount","type":"uint256"},{"components":[{"internalType":"uint16","name":"bank","type":"uint16"},{"internalType":"uint16","name":"dividend","type":"uint16"},{"internalType":"uint16","name":"partner","type":"uint16"},{"internalType":"uint16","name":"treasury","type":"uint16"},{"internalType":"uint16","name":"team","type":"uint16"},{"internalType":"uint256","name":"dividendAmount","type":"uint256"},{"internalType":"uint256","name":"partnerAmount","type":"uint256"},{"internalType":"uint256","name":"treasuryAmount","type":"uint256"},{"internalType":"uint256","name":"teamAmount","type":"uint256"}],"internalType":"struct Bank.HouseEdgeSplit","name":"houseEdgeSplit","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"treasury","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"}],"name":"withdrawHouseEdgeAmount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"}],"name":"withdrawPartnerAmount","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60a06040523480156200001157600080fd5b5060405162004daf38038062004daf83398101604081905262000034916200050c565b6001600160a01b0382166200005c57604051630d23cf4160e11b815260040160405180910390fd5b6001600160a01b0382166080526200007660003362000089565b620000818162000099565b5050620006ac565b6200009582826200012e565b5050565b6000620000a68162000171565b6001600160a01b038216620000ce57604051630d23cf4160e11b815260040160405180910390fd5b60028054610100600160a81b0319166101006001600160a01b038581168202929092179283905560405192041681527fc6a5dd316fe9d0339f2769deab7e31f64c8f5b101ffd85dfc9a83dbeaf2e69da9060200160405180910390a15050565b6200014582826200018060201b62002bdd1760201c565b60008281526001602090815260409091206200016c91839062002c9962000220821b17901c565b505050565b6200017d813362000240565b50565b6000828152602081815260408083206001600160a01b038516845290915290205460ff1662000095576000828152602081815260408083206001600160a01b03851684529091529020805460ff19166001179055620001dc3390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b600062000237836001600160a01b038416620002e4565b90505b92915050565b6000828152602081815260408083206001600160a01b038516845290915290205460ff1662000095576200028a816001600160a01b031660146200033660201b62002cae1760201c565b620002a083602062002cae62000336821b17811c565b604051602001620002b39291906200056a565b60408051601f198184030181529082905262461bcd60e51b8252620002db91600401620005e3565b60405180910390fd5b60008181526001830160205260408120546200032d575081546001818101845560008481526020808220909301849055845484825282860190935260409020919091556200023a565b5060006200023a565b60606000620003478360026200062e565b6200035490600262000650565b6001600160401b038111156200036e576200036e62000666565b6040519080825280601f01601f19166020018201604052801562000399576020820181803683370190505b509050600360fc1b81600081518110620003b757620003b76200067c565b60200101906001600160f81b031916908160001a905350600f60fb1b81600181518110620003e957620003e96200067c565b60200101906001600160f81b031916908160001a90535060006200040f8460026200062e565b6200041c90600162000650565b90505b60018111156200049e576f181899199a1a9b1b9c1cb0b131b232b360811b85600f16601081106200045457620004546200067c565b1a60f81b8282815181106200046d576200046d6200067c565b60200101906001600160f81b031916908160001a90535060049490941c93620004968162000692565b90506200041f565b508315620002375760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401620002db565b80516001600160a01b03811681146200050757600080fd5b919050565b600080604083850312156200052057600080fd5b6200052b83620004ef565b91506200053b60208401620004ef565b90509250929050565b60005b838110156200056157818101518382015260200162000547565b50506000910152565b7f416363657373436f6e74726f6c3a206163636f756e7420000000000000000000815260008351620005a481601785016020880162000544565b7001034b99036b4b9b9b4b733903937b6329607d1b6017918401918201528351620005d781602884016020880162000544565b01602801949350505050565b60208152600082518060208401526200060481604085016020870162000544565b601f01601f19169190910160400192915050565b634e487b7160e01b600052601160045260246000fd5b60008160001904831182151516156200064b576200064b62000618565b500290565b808201808211156200023a576200023a62000618565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b600081620006a457620006a462000618565b506000190190565b6080516146e0620006cf6000396000818161057f0152610a9801526146e06000f3fe6080604052600436106102dc5760003560e01c8063802ad8fb11610184578063ca7a8769116100d6578063e48603391161008a578063f6ae220411610064578063f6ae22041461098b578063f6b2a2f1146109bf578063f8b2cb4f146109df57600080fd5b8063e486033914610841578063ed442e1414610956578063f3fef3a31461096b57600080fd5b8063d48bfca7116100bb578063d48bfca7146107e1578063d547741f14610801578063dc17c9ca1461082157600080fd5b8063ca7a8769146107ae578063cbe230c3146107c157600080fd5b8063aa6ca80811610138578063bc3ec38311610112578063bc3ec3831461074e578063c437afde1461076e578063ca15c8731461078e57600080fd5b8063aa6ca808146106cb578063ab400706146106ed578063ac9650d81461072157600080fd5b80639010d07c116101695780639010d07c1461065257806391d1485414610672578063a217fddf146106b657600080fd5b8063802ad8fb1461060f5780638aaa22841461063257600080fd5b806336568abe1161023d57806356f0406d116101f1578063659bdfc6116101cb578063659bdfc6146105a15780636dfa8e52146105c15780636e04ff0d146105e157600080fd5b806356f0406d14610510578063599270441461053057806361d027b31461056d57600080fd5b80634297dcbe116102225780634297dcbe146104bd5780634585e33b146104dd57806347e7ef24146104fd57600080fd5b806336568abe1461047d57806338c64d2f1461049d57600080fd5b8063278b39de116102945780632ee99d3e116102795780632ee99d3e1461042a5780632f2ff15d1461044a5780633138809d1461046a57600080fd5b8063278b39de146103a9578063279e3415146103c957600080fd5b80631525ff7d116102c55780631525ff7d146103385780631f61502314610358578063248a9ca31461036b57600080fd5b806301ffc9a7146102e15780630ae30cb014610316575b600080fd5b3480156102ed57600080fd5b506103016102fc366004613a8f565b6109ff565b60405190151581526020015b60405180910390f35b34801561032257600080fd5b50610336610331366004613ae6565b610a5b565b005b34801561034457600080fd5b50610336610353366004613ae6565b610b44565b610336610366366004613b03565b610c07565b34801561037757600080fd5b5061039b610386366004613b49565b60009081526020819052604090206001015490565b60405190815260200161030d565b3480156103b557600080fd5b5061039b6103c4366004613b62565b610c99565b3480156103d557600080fd5b506104116103e4366004613ae6565b6001600160a01b0316600090815260036020526040902054640100000000900467ffffffffffffffff1690565b60405167ffffffffffffffff909116815260200161030d565b34801561043657600080fd5b50610336610445366004613ba0565b610ce2565b34801561045657600080fd5b50610336610465366004613c16565b610ea9565b610336610478366004613b62565b610ed3565b34801561048957600080fd5b50610336610498366004613c16565b610f66565b3480156104a957600080fd5b506103366104b8366004613ae6565b61100c565b3480156104c957600080fd5b506103366104d8366004613b62565b6110be565b3480156104e957600080fd5b506103366104f8366004613c46565b611193565b61033661050b366004613b62565b61130a565b34801561051c57600080fd5b5061033661052b366004613cb8565b6113f5565b34801561053c57600080fd5b506002546105559061010090046001600160a01b031681565b6040516001600160a01b03909116815260200161030d565b34801561057957600080fd5b506105557f000000000000000000000000000000000000000000000000000000000000000081565b3480156105ad57600080fd5b506103366105bc366004613b62565b6114fa565b3480156105cd57600080fd5b506103366105dc366004613ced565b6115cf565b3480156105ed57600080fd5b506106016105fc366004613c46565b6116dc565b60405161030d929190613d78565b34801561061b57600080fd5b50610624611871565b60405161030d929190613d93565b34801561063e57600080fd5b5061033661064d366004613e25565b611a50565b34801561065e57600080fd5b5061055561066d366004613e53565b611ad1565b34801561067e57600080fd5b5061030161068d366004613c16565b6000918252602082815260408084206001600160a01b0393909316845291905290205460ff1690565b3480156106c257600080fd5b5061039b600081565b3480156106d757600080fd5b506106e0611ae9565b60405161030d9190613eed565b3480156106f957600080fd5b5061039b7f6a64baf327d646d1bca72653e2a075d15fd6ac6d8cbd7f6ee03fc55875e0fa8881565b34801561072d57600080fd5b5061074161073c36600461403a565b611f05565b60405161030d919061409d565b34801561075a57600080fd5b5061033661076936600461411d565b611ffa565b34801561077a57600080fd5b50610336610789366004613e25565b612200565b34801561079a57600080fd5b5061039b6107a9366004613b49565b612301565b6103366107bc366004613b62565b612318565b3480156107cd57600080fd5b506103016107dc366004613ae6565b612380565b3480156107ed57600080fd5b506103366107fc366004613ae6565b6124bc565b34801561080d57600080fd5b5061033661081c366004613c16565b612601565b34801561082d57600080fd5b5061055561083c366004613ae6565b612626565b34801561084d57600080fd5b5061094261085c366004613ae6565b6003602081815260009283526040928390208054600182015460028301548651610120810188529584015461ffff80821688526201000080830482169789019790975264010000000080830482169989019990995266010000000000008204811660608901526801000000000000000090910481166080880152600485015460a0880152600585015460c0880152600685015460e08801526007909401546101008088019190915260ff8085169891850416969584049094169493830467ffffffffffffffff16936c010000000000000000000000009093046001600160a01b03169288565b60405161030d98979695949392919061414b565b34801561096257600080fd5b50610624612664565b34801561097757600080fd5b50610336610986366004613b62565b6127c5565b34801561099757600080fd5b5061039b7f39780592ddb1e73f2a037f166ec57ba042082746f5c0b9fe56ea386e00369a9881565b3480156109cb57600080fd5b5061039b6109da366004613ae6565b612a39565b3480156109eb57600080fd5b5061039b6109fa366004613ae6565b612a67565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f5a05180f000000000000000000000000000000000000000000000000000000001480610a555750610a5582612ef1565b92915050565b6001600160a01b0381166000908152600360208190526040909120600681015460078201549190920191908115610abe5760006003840155610abe7f00000000000000000000000000000000000000000000000000000000000000008584612f88565b8015610ae75760006004840155600254610ae79061010090046001600160a01b03168583612f88565b81151580610af457508015155b15610b3e5760408051838152602081018390526001600160a01b038616917f0146f1701c23c89f761280798d36d6c4e3acb349438456f5da8f83a2f5dd8cdc910160405180910390a25b50505050565b6000610b4f81612fe0565b6001600160a01b038216610b8f576040517f1a479e8200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600280547fffffffffffffffffffffff0000000000000000000000000000000000000000ff166101006001600160a01b038581168202929092179283905560405192041681527fc6a5dd316fe9d0339f2769deab7e31f64c8f5b101ffd85dfc9a83dbeaf2e69da906020015b60405180910390a15050565b7f6a64baf327d646d1bca72653e2a075d15fd6ac6d8cbd7f6ee03fc55875e0fa88610c3181612fe0565b610c3b8483612fed565b610c46858585612f88565b836001600160a01b03167f634235fcf5af0adbca1a405ec65f6f6c08f55e1f379c2c45cd10f23cb29e0e31610c7a86612a67565b6040805191825260208201879052015b60405180910390a25050505050565b6001600160a01b038216600090815260036020526040812054829062010000900461ffff16610cc785612a67565b610cd191906141dd565b610cdb919061421a565b9392505050565b6000610ced81612fe0565b6000838584610cfc898b614255565b610d069190614255565b610d109190614255565b610d1a9190614255565b90508061ffff1661271014610d66576040517f6104384400000000000000000000000000000000000000000000000000000000815261ffff821660048201526024015b60405180910390fd5b6000600360008a6001600160a01b03166001600160a01b031681526020019081526020016000206003019050878160000160006101000a81548161ffff021916908361ffff160217905550868160000160026101000a81548161ffff021916908361ffff160217905550858160000160046101000a81548161ffff021916908361ffff160217905550848160000160066101000a81548161ffff021916908361ffff160217905550838160000160086101000a81548161ffff021916908361ffff160217905550886001600160a01b03167f87512489f5b5226512d8f1bfede20d9e809ff4042f028da79c833ed04baa83978989898989604051610e9695949392919061ffff95861681529385166020850152918416604084015283166060830152909116608082015260a00190565b60405180910390a2505050505050505050565b600082815260208190526040902060010154610ec481612fe0565b610ece83836131b8565b505050565b7f6a64baf327d646d1bca72653e2a075d15fd6ac6d8cbd7f6ee03fc55875e0fa88610efd81612fe0565b826001600160a01b03167f812b76b477469edc716929cbf7ed54e3d9c1a68d8b9f8290dbabcda54d96fcbe610f3185612a67565b6001600160a01b03861615610f465784610f48565b345b604080519283526020830191909152015b60405180910390a2505050565b6001600160a01b0381163314610ffe576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201527f20726f6c657320666f722073656c6600000000000000000000000000000000006064820152608401610d5d565b61100882826131da565b5050565b6001600160a01b038116600090815260036020526040902060058101548015801590611051575081546c0100000000000000000000000090046001600160a01b031615155b15610ece57600060058301558154611083906c0100000000000000000000000090046001600160a01b03168483612f88565b826001600160a01b03167f1aa0fc6b9bc54a055d93b640b2c7313e0bd2944482e0b6b5fec7048bbdaacd8182604051610f5991815260200190565b6001600160a01b03808316600090815260036020526040812054909184916c0100000000000000000000000090041680611101576110fc83336131fc565b611143565b336001600160a01b03821614611143576040517f4ca8886700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b03851660008181526003602052604090819020600101869055517f8b4a07860448b613116df3f175c837980eceaca7d1dc5143c46a20ab31ec00f990610c8a9087815260200190565b6000806111a283850185614270565b6001600160a01b03818116600090815260036020818152604080842081516101008082018452825460ff80821615158452828204161515838701526201000080820461ffff9081168588015264010000000080840467ffffffffffffffff166060808801919091526c01000000000000000000000000909404909b16608080870191909152600187015460a080880191909152600288015460c080890191909152895161012081018b529b8901548085168d5294850484169a8c019a909a529b83048216978a0197909752660100000000000082048116928901929092526801000000000000000090041693860193909352600482015496850196909652600581015492840192909252600682015460e08085019190915260079092015490830152830152929450909250908380156112dd576112dd614292565b1480156112f557508060c001518160e0015160c00151115b15611303576113038261100c565b5050505050565b6001600160a01b03808316600090815260036020526040812054909184916c010000000000000000000000009004168061134d5761134883336131fc565b61138f565b336001600160a01b0382161461138f576040517f4ca8886700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b0385166113a5573493506113ba565b6113ba6001600160a01b038616333087613294565b846001600160a01b03167fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c85604051610c8a91815260200190565b6001600160a01b03808316600090815260036020526040812054909184916c01000000000000000000000000900416806114385761143383336131fc565b61147a565b336001600160a01b0382161461147a576040517f4ca8886700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b03851660008181526003602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000ffff166201000061ffff8a169081029190911790915591519182527f68288b0936dd994d19323ee940b2703e7bf46fae2552319d60dc1239ba52ec689101610c8a565b6001600160a01b03808316600090815260036020526040812054909184916c010000000000000000000000009004168061153d5761153883336131fc565b61157f565b336001600160a01b0382161461157f576040517f4ca8886700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b03851660008181526003602052604090819020600201869055517fc299d8f578e9777556514dc79b1c053fe9912dbc8424518c97262734a96e809590610c8a9087815260200190565b6001600160a01b03808316600090815260036020526040812054909184916c01000000000000000000000000900416806116125761160d83336131fc565b611654565b336001600160a01b03821614611654576040517f4ca8886700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b03851660008181526003602090815260409182902080547fffffffffffffffffffffffffffffffffffffffff0000000000000000ffffffff1664010000000067ffffffffffffffff8a169081029190911790915591519182527fae367543cf47ca7e51a9f3f92711553aa33777847f719f7e2821ea8d846ed0e19101610c8a565b6000606081806116ee85870187614270565b9092509050600082801561170457611704614292565b03611868576001600160a01b0381811660009081526003602081815260409283902083516101008082018652825460ff80821615158452828204161515838601526201000080820461ffff908116858a015264010000000080840467ffffffffffffffff166060808801919091526c01000000000000000000000000909404909a16608080870191909152600187015460a080880191909152600288015460c08089019182528c5161012081018e529b8a01548086168d5295860485169a8c019a909a529b840483169a8a019a909a52660100000000000083048216938901939093526801000000000000000090910416908601526004830154958501959095526005820154928401928352600682015460e080860191909152600790920154948401949094528301919091529151915190911015611866576001945082826040516020016118549291906142c1565b60405160208183030381529060405293505b505b50509250929050565b6060807f39780592ddb1e73f2a037f166ec57ba042082746f5c0b9fe56ea386e00369a9861189e81612fe0565b60025460009060ff1667ffffffffffffffff8111156118bf576118bf614314565b6040519080825280602002602001820160405280156118e8578160200160208202803683370190505b5060025490915060009060ff1667ffffffffffffffff81111561190d5761190d614314565b604051908082528060200260200182016040528015611936578160200160208202803683370190505b50905060005b60025460ff9081169082161015611a455760ff81166000908152600460208181526040808420546001600160a01b03168085526003909252909220908101548015611a2f5760006004830155611993338483612f88565b826001600160a01b03167fca64dbcaf91abfb066e7a5163f1d135f8f48f2cbdb0395e3b35cc278ebbd340b826040516119ce91815260200190565b60405180910390a282868560ff16815181106119ec576119ec614343565b60200260200101906001600160a01b031690816001600160a01b03168152505080858560ff1681518110611a2257611a22614343565b6020026020010181815250505b5050508080611a3d90614372565b91505061193c565b509093509150509091565b6000611a5b81612fe0565b6001600160a01b03831660008181526003602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001686151590811790915591519182527fe589eb036e62c07e307aa4d441bd39c81e8bd86f349eaacb0caa06b1477f7f9a9101610f59565b6000828152600160205260408120610cdb9083613345565b60025460609060009060ff1667ffffffffffffffff811115611b0d57611b0d614314565b604051908082528060200260200182016040528015611b4657816020015b611b336139d2565b815260200190600190039081611b2b5790505b50905060005b60025460ff9081169082161015611eff5760ff8181166000908152600460208181526040808420546001600160a01b03908116808652600380855295839020835161010080820186528254808b1615158352818104909a1615158288015262010000808b0461ffff90811684890152640100000000808d0467ffffffffffffffff166060808701919091526c01000000000000000000000000909d04909716608080860191909152600186015460a080870191909152600287015460c0808801919091528a5161012081018c529d8801548085168f5294850484169b8e019b909b529783048216988c01989098526601000000000000820481169b8b019b909b52680100000000000000009004909916948801949094529485015491860191909152600584015492850192909252600683015460e08086019190915260079093015494840194909452908301919091529081611d65576040518060a00160405280601260ff168152602001836001600160a01b031681526020016040518060400160405280600381526020017f455448000000000000000000000000000000000000000000000000000000000081525081526020016040518060400160405280600381526020017f4554480000000000000000000000000000000000000000000000000000000000815250815260200182815250848460ff1681518110611d5557611d55614343565b6020026020010181905250611eea565b60008290506040518060a00160405280826001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015611db3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611dd79190614391565b60ff168152602001846001600160a01b03168152602001826001600160a01b03166306fdde036040518163ffffffff1660e01b8152600401600060405180830381865afa158015611e2c573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611e5491908101906143b4565b8152602001826001600160a01b03166395d89b416040518163ffffffff1660e01b8152600401600060405180830381865afa158015611e97573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611ebf91908101906143b4565b815260200183815250858560ff1681518110611edd57611edd614343565b6020026020010181905250505b50508080611ef790614372565b915050611b4c565b50919050565b60608167ffffffffffffffff811115611f2057611f20614314565b604051908082528060200260200182016040528015611f5357816020015b6060815260200190600190039081611f3e5790505b50905060005b82811015611ff357611fc330858584818110611f7757611f77614343565b9050602002810190611f899190614456565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061335192505050565b828281518110611fd557611fd5614343565b60200260200101819052508080611feb906144c2565b915050611f59565b5092915050565b6001600160a01b03808316600090815260036020526040812054909184916c010000000000000000000000009004168061203d5761203883336131fc565b61207f565b336001600160a01b0382161461207f576040517f4ca8886700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006120aa7f6a64baf327d646d1bca72653e2a075d15fd6ac6d8cbd7f6ee03fc55875e0fa88612301565b905060005b8181101561216a576120e17f6a64baf327d646d1bca72653e2a075d15fd6ac6d8cbd7f6ee03fc55875e0fa8882611ad1565b6040517f707cd55e0000000000000000000000000000000000000000000000000000000081526001600160a01b038981166004830152919091169063707cd55e90602401600060405180830381600087803b15801561213f57600080fd5b505af1158015612153573d6000803e3d6000fd5b505050508080612162906144c2565b9150506120af565b506121748661100c565b6121818661098688612a67565b6001600160a01b0386811660008181526003602090815260409182902080546bffffffffffffffffffffffff166c01000000000000000000000000958b16958602179055905192835290917fc991529379552af6d0789cc899d5180a270a4bc3b1a3892adcac3d95f93dda1d91015b60405180910390a2505050505050565b6001600160a01b03808316600090815260036020526040812054909184916c01000000000000000000000000900416806122435761223e83336131fc565b612285565b336001600160a01b03821614612285576040517f4ca8886700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038516600081815260036020526040908190208054871515610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff909116179055517f624dd6809915867c3c11279c2699633040681f0e4efb0fa9a193217a11903c6090610c8a90871515815260200190565b6000818152600160205260408120610a5590613376565b7f6a64baf327d646d1bca72653e2a075d15fd6ac6d8cbd7f6ee03fc55875e0fa8861234281612fe0565b61234c8383612fed565b826001600160a01b03167f71928ab56dd9fb6df8072adf5fefb4d17ed280fa21652433fe96d7d6729bd328610f3185612a67565b6001600160a01b03818116600090815260036020818152604080842081516101008082018452825460ff80821615158452828204161515838701526201000080820461ffff9081168588015264010000000080840467ffffffffffffffff166060808801919091526c01000000000000000000000000909404909b16608080870191909152600187015460a080880191909152600288015460c080890191909152895161012081018b529b8901548085168d5294850484169a8c019a909a529b83048216978a0197909752660100000000000082048116928901929092526801000000000000000090041693860193909352600482015496850196909652600581015492840192909252600682015460e0808501919091526007909201549083015283015281519091908015610cdb5750602001511592915050565b60006124c781612fe0565b60025460ff16156125515760005b60025460ff908116908216101561254f5760ff81166000908152600460205260409020546001600160a01b0380851691160361253d576040517f55c7e8ba00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8061254781614372565b9150506124d5565b505b6002805460ff908116600090815260046020526040812080547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0387161790558254600193926125ab918591166144fa565b92506101000a81548160ff021916908360ff1602179055507fe473c74f34be27c1464d6624f14a0d7fd4e301cbfa29c3eba425d378c8a7ebe082604051610bfb91906001600160a01b0391909116815260200190565b60008281526020819052604090206001015461261c81612fe0565b610ece83836131da565b6001600160a01b0380821660009081526003602052604081205490916c010000000000000000000000009091041680610a5557610cdb600080611ad1565b600254606090819060009060ff1667ffffffffffffffff81111561268a5761268a614314565b6040519080825280602002602001820160405280156126b3578160200160208202803683370190505b5060025490915060009060ff1667ffffffffffffffff8111156126d8576126d8614314565b604051908082528060200260200182016040528015612701578160200160208202803683370190505b50905060005b60025460ff90811690821610156127bb5760ff81166000908152600460208181526040808420546001600160a01b031680855260039092529092209081015480156127a55782868560ff168151811061276257612762614343565b60200260200101906001600160a01b031690816001600160a01b03168152505080858560ff168151811061279857612798614343565b6020026020010181815250505b50505080806127b390614372565b915050612707565b5090939092509050565b6001600160a01b03808316600090815260036020526040812054909184916c01000000000000000000000000900416806128085761280383336131fc565b61284a565b336001600160a01b0382161461284a576040517f4ca8886700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600061285586612a67565b905080156129e7576001600160a01b038616600090815260036020526040902054610100900460ff166128b4576040517f93b3a85d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006128df7f6a64baf327d646d1bca72653e2a075d15fd6ac6d8cbd7f6ee03fc55875e0fa88612301565b905060005b818110156129e4576129167f6a64baf327d646d1bca72653e2a075d15fd6ac6d8cbd7f6ee03fc55875e0fa8882611ad1565b6040517f594043fd0000000000000000000000000000000000000000000000000000000081526001600160a01b038a81166004830152919091169063594043fd90602401602060405180830381865afa158015612977573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061299b9190614513565b156129d2576040517faa4256e000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b806129dc816144c2565b9150506128e4565b50505b808511156129f3578094505b6129fe338787612f88565b856001600160a01b03167f884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364866040516121f091815260200190565b6001600160a01b03811660009081526003602052604081206001015490819003612a6257506127105b919050565b6000806001600160a01b038316612a7f575047612b03565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b038416906370a0823190602401602060405180830381865afa158015612adc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b009190614530565b90505b6001600160a01b0383166000908152600360208181526040928390208351610120810185529281015461ffff80821685526201000082048116938501939093526401000000008104831694840194909452660100000000000084048216606084015268010000000000000000909304166080820152600482015460a08201819052600583015460c08301819052600684015460e084018190526007909401546101008401819052929391612bb79086614549565b612bc19190614549565b612bcb9190614549565b612bd59190614549565b949350505050565b6000828152602081815260408083206001600160a01b038516845290915290205460ff16611008576000828152602081815260408083206001600160a01b0385168452909152902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055612c553390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b6000610cdb836001600160a01b038416613380565b60606000612cbd8360026141dd565b612cc890600261455c565b67ffffffffffffffff811115612ce057612ce0614314565b6040519080825280601f01601f191660200182016040528015612d0a576020820181803683370190505b5090507f300000000000000000000000000000000000000000000000000000000000000081600081518110612d4157612d41614343565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053507f780000000000000000000000000000000000000000000000000000000000000081600181518110612da457612da4614343565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053506000612de08460026141dd565b612deb90600161455c565b90505b6001811115612e88577f303132333435363738396162636465660000000000000000000000000000000085600f1660108110612e2c57612e2c614343565b1a60f81b828281518110612e4257612e42614343565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535060049490941c93612e818161456f565b9050612dee565b508315610cdb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610d5d565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f7965db0b000000000000000000000000000000000000000000000000000000001480610a5557507f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff00000000000000000000000000000000000000000000000000000000831614610a55565b6001600160a01b038216612fcc576040516001600160a01b0384169082156108fc029083906000818181858888f19350505050158015610b3e573d6000803e3d6000fd5b610ece6001600160a01b03831684836133cf565b612fea81336131fc565b50565b6001600160a01b03821660009081526003602081905260408220018054909190640100000000900461ffff16156130605781546127109061303a90640100000000900461ffff16856141dd565b613044919061421a565b90508082600201600082825461305a919061455c565b90915550505b81546000906127109061307d9062010000900461ffff16866141dd565b613087919061421a565b90508083600101600082825461309d919061455c565b90915550508254600090612710906130b99061ffff16876141dd565b6130c3919061421a565b8454909150600090612710906130e7906601000000000000900461ffff16886141dd565b6130f1919061421a565b905080856003016000828254613107919061455c565b909155505084546000906127109061312f9068010000000000000000900461ffff16896141dd565b613139919061421a565b90508086600401600082825461314f919061455c565b9091555050604080518481526020810186905290810186905260608101839052608081018290526001600160a01b038916907fc73e6f5bcd4395126567ead7e4c14b7c52a5f0b9eabd3335a05e631646a127e39060a00160405180910390a25050505050505050565b6131c28282612bdd565b6000828152600160205260409020610ece9082612c99565b6131e48282613418565b6000828152600160205260409020610ece90826134b5565b6000828152602081815260408083206001600160a01b038516845290915290205460ff1661100857613238816001600160a01b03166014612cae565b613243836020612cae565b6040516020016132549291906145a4565b60408051601f19818403018152908290527f08c379a0000000000000000000000000000000000000000000000000000000008252610d5d91600401614625565b6040516001600160a01b0380851660248301528316604482015260648101829052610b3e9085907f23b872dd00000000000000000000000000000000000000000000000000000000906084015b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909316929092179091526134ca565b6000610cdb83836135c9565b6060610cdb8383604051806060016040528060278152602001614684602791396135f3565b6000610a55825490565b60008181526001830160205260408120546133c757508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610a55565b506000610a55565b6040516001600160a01b038316602482015260448101829052610ece9084907fa9059cbb00000000000000000000000000000000000000000000000000000000906064016132e1565b6000828152602081815260408083206001600160a01b038516845290915290205460ff1615611008576000828152602081815260408083206001600160a01b038516808552925280832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b6000610cdb836001600160a01b038416613701565b600061351f826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166137f49092919063ffffffff16565b805190915015610ece578080602001905181019061353d9190614513565b610ece576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401610d5d565b60008260000182815481106135e0576135e0614343565b9060005260206000200154905092915050565b60606001600160a01b0384163b61368c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f60448201527f6e747261637400000000000000000000000000000000000000000000000000006064820152608401610d5d565b600080856001600160a01b0316856040516136a79190614638565b600060405180830381855af49150503d80600081146136e2576040519150601f19603f3d011682016040523d82523d6000602084013e6136e7565b606091505b50915091506136f7828286613803565b9695505050505050565b600081815260018301602052604081205480156137ea576000613725600183614549565b855490915060009061373990600190614549565b905081811461379e57600086600001828154811061375957613759614343565b906000526020600020015490508087600001848154811061377c5761377c614343565b6000918252602080832090910192909255918252600188019052604090208390555b85548690806137af576137af614654565b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050610a55565b6000915050610a55565b6060612bd58484600085613856565b60608315613812575081610cdb565b8251156138225782518084602001fd5b816040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610d5d9190614625565b6060824710156138e8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c00000000000000000000000000000000000000000000000000006064820152608401610d5d565b6001600160a01b0385163b613959576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610d5d565b600080866001600160a01b031685876040516139759190614638565b60006040518083038185875af1925050503d80600081146139b2576040519150601f19603f3d011682016040523d82523d6000602084013e6139b7565b606091505b50915091506139c7828286613803565b979650505050505050565b6040518060a00160405280600060ff16815260200160006001600160a01b031681526020016060815260200160608152602001613a8a604080516101008082018352600080835260208084018290528385018290526060808501839052608080860184905260a080870185905260c0808801869052885161012081018a528681529485018690529784018590529183018490528201839052810182905293840181905260e08481018290529184015290919082015290565b905290565b600060208284031215613aa157600080fd5b81357fffffffff0000000000000000000000000000000000000000000000000000000081168114610cdb57600080fd5b6001600160a01b0381168114612fea57600080fd5b600060208284031215613af857600080fd5b8135610cdb81613ad1565b60008060008060808587031215613b1957600080fd5b8435613b2481613ad1565b93506020850135613b3481613ad1565b93969395505050506040820135916060013590565b600060208284031215613b5b57600080fd5b5035919050565b60008060408385031215613b7557600080fd5b8235613b8081613ad1565b946020939093013593505050565b803561ffff81168114612a6257600080fd5b60008060008060008060c08789031215613bb957600080fd5b8635613bc481613ad1565b9550613bd260208801613b8e565b9450613be060408801613b8e565b9350613bee60608801613b8e565b9250613bfc60808801613b8e565b9150613c0a60a08801613b8e565b90509295509295509295565b60008060408385031215613c2957600080fd5b823591506020830135613c3b81613ad1565b809150509250929050565b60008060208385031215613c5957600080fd5b823567ffffffffffffffff80821115613c7157600080fd5b818501915085601f830112613c8557600080fd5b813581811115613c9457600080fd5b866020828501011115613ca657600080fd5b60209290920196919550909350505050565b60008060408385031215613ccb57600080fd5b8235613cd681613ad1565b9150613ce460208401613b8e565b90509250929050565b60008060408385031215613d0057600080fd5b8235613d0b81613ad1565b9150602083013567ffffffffffffffff81168114613c3b57600080fd5b60005b83811015613d43578181015183820152602001613d2b565b50506000910152565b60008151808452613d64816020860160208601613d28565b601f01601f19169290920160200192915050565b8215158152604060208201526000612bd56040830184613d4c565b604080825283519082018190526000906020906060840190828701845b82811015613dd55781516001600160a01b031684529284019290840190600101613db0565b5050508381038285015284518082528583019183019060005b81811015613e0a57835183529284019291840191600101613dee565b5090979650505050505050565b8015158114612fea57600080fd5b60008060408385031215613e3857600080fd5b8235613e4381613ad1565b91506020830135613c3b81613e17565b60008060408385031215613e6657600080fd5b50508035926020909101359150565b61ffff808251168352806020830151166020840152806040830151166040840152506060810151613eac606084018261ffff169052565b506080810151613ec2608084018261ffff169052565b5060a0818101519083015260c0808201519083015260e0808201519083015261010090810151910152565b60006020808301818452808551808352604092508286019150828160051b87010184880160005b8381101561402c577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0898403018552815161028060ff82511685526001600160a01b03898301511689860152878201518189870152613f7582870182613d4c565b91505060608083015186830382880152613f8f8382613d4c565b925050608080840151935083511515818801528a84015160a0811515818a01528b860151915060c061ffff8316818b015284870151945060e09250613fdf838b018667ffffffffffffffff169052565b928601516001600160a01b03166101008a015285015161012089015290840151610140880152909201519150614019610160860183613e75565b9588019593505090860190600101613f14565b509098975050505050505050565b6000806020838503121561404d57600080fd5b823567ffffffffffffffff8082111561406557600080fd5b818501915085601f83011261407957600080fd5b81358181111561408857600080fd5b8660208260051b8501011115613ca657600080fd5b6000602080830181845280855180835260408601915060408160051b870101925083870160005b82811015614110577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08886030184526140fe858351613d4c565b945092850192908501906001016140c4565b5092979650505050505050565b6000806040838503121561413057600080fd5b823561413b81613ad1565b91506020830135613c3b81613ad1565b8815158152871515602082015261ffff8716604082015267ffffffffffffffff861660608201526001600160a01b038516608082015260a0810184905260c0810183905261020081016141a160e0830184613e75565b9998505050505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615614215576142156141ae565b500290565b600082614250577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b61ffff818116838216019080821115611ff357611ff36141ae565b6000806040838503121561428357600080fd5b82356001811061413b57600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b60408101600184106142fc577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b9281526001600160a01b039190911660209091015290565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600060ff821660ff8103614388576143886141ae565b60010192915050565b6000602082840312156143a357600080fd5b815160ff81168114610cdb57600080fd5b6000602082840312156143c657600080fd5b815167ffffffffffffffff808211156143de57600080fd5b818401915084601f8301126143f257600080fd5b81518181111561440457614404614314565b604051601f8201601f19908116603f0116810190838211818310171561442c5761442c614314565b8160405282815287602084870101111561444557600080fd5b6139c7836020830160208801613d28565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261448b57600080fd5b83018035915067ffffffffffffffff8211156144a657600080fd5b6020019150368190038213156144bb57600080fd5b9250929050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036144f3576144f36141ae565b5060010190565b60ff8181168382160190811115610a5557610a556141ae565b60006020828403121561452557600080fd5b8151610cdb81613e17565b60006020828403121561454257600080fd5b5051919050565b81810381811115610a5557610a556141ae565b80820180821115610a5557610a556141ae565b60008161457e5761457e6141ae565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b7f416363657373436f6e74726f6c3a206163636f756e74200000000000000000008152600083516145dc816017850160208801613d28565b7f206973206d697373696e6720726f6c65200000000000000000000000000000006017918401918201528351614619816028840160208801613d28565b01602801949350505050565b602081526000610cdb6020830184613d4c565b6000825161464a818460208701613d28565b9190910192915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fdfe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a26469706673582212202f7826b8001562f5e0ff2a1a1f85eb954d075cf227561a3e8cc4a2757df6cca564736f6c634300081000330000000000000000000000002c305888a456e7004663bc12a74395e637eabcbc0000000000000000000000008343f2dc53e55dbbe626b9d8b4fbbb4e37dc218b

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

0000000000000000000000002c305888a456e7004663bc12a74395e637eabcbc0000000000000000000000008343f2dc53e55dbbe626b9d8b4fbbb4e37dc218b

-----Decoded View---------------
Arg [0] : treasuryAddress (address): 0x2c305888a456e7004663bc12a74395e637eabcbc
Arg [1] : teamWalletAddress (address): 0x8343f2dc53e55dbbe626b9d8b4fbbb4e37dc218b

-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 0000000000000000000000002c305888a456e7004663bc12a74395e637eabcbc
Arg [1] : 0000000000000000000000008343f2dc53e55dbbe626b9d8b4fbbb4e37dc218b


Block Transaction Gas Used Reward
Age Block Fee Address BC Fee Address Voting Power Jailed Incoming
Block Uncle Number Difficulty Gas Used Reward
Loading