Contract 0x887a3c3d25eb56953124aede7106c65c8b91720c

Contract Overview

Balance:
0.099714852146540607 MATIC

Token:
Txn Hash
Method
Block
From
To
Value [Txn Fee]
0x771370d517aa8647935de0c6fc6de87b1b4ddd0479e547e39757f0c0e121980fWithdraw277868252022-08-26 14:00:21283 days 22 hrs ago0x2c305888a456e7004663bc12a74395e637eabcbc IN  0x887a3c3d25eb56953124aede7106c65c8b91720c0 MATIC0.0001586075 2.500000007
0xc3e7634fc96845c1b0a5ed6b8dc46cc2423848cd635a25fcdf81ed3e39dd33f8Withdraw House E...277868252022-08-26 14:00:21283 days 22 hrs ago0x2c305888a456e7004663bc12a74395e637eabcbc IN  0x887a3c3d25eb56953124aede7106c65c8b91720c0 MATIC0.0001749975 2.500000007
0x43fb332e7b5d13b9d968f366fb3cbdbcf93777e4dca104b7cccbfab896cf51f5Withdraw277868252022-08-26 14:00:21283 days 22 hrs ago0x2c305888a456e7004663bc12a74395e637eabcbc IN  0x887a3c3d25eb56953124aede7106c65c8b91720c0 MATIC0.000130985 2.500000007
0x3b73f2efcf6b1b234d0d6ae2885557e9cf08517c3c74c024d15b4073a17c2bdeWithdraw House E...277868252022-08-26 14:00:21283 days 22 hrs ago0x2c305888a456e7004663bc12a74395e637eabcbc IN  0x887a3c3d25eb56953124aede7106c65c8b91720c0 MATIC0.0001102875 2.500000007
0x84a185b75f8e57dfcf7db8f12ed732c309e97f46b59a41fd03f39770933a0598Grant Role271858722022-07-15 14:38:39325 days 21 hrs ago0x2c305888a456e7004663bc12a74395e637eabcbc IN  0x887a3c3d25eb56953124aede7106c65c8b91720c0 MATIC0.001750903997 34.080193042
0xbd885aeb18a8b974e5e1905aa1edad02254bf9fb7a6529211667a371fb4e7e2fSet Balance Risk267286702022-06-13 15:11:48357 days 21 hrs ago0x2c305888a456e7004663bc12a74395e637eabcbc IN  0x887a3c3d25eb56953124aede7106c65c8b91720c0 MATIC0.000058321152 1.868848428
0x16a5d976549ee1416aa789c259d919d2a47f3a8f8e093e12110048fec0cec409Set Allowed Toke...267286682022-06-13 15:11:38357 days 21 hrs ago0x2c305888a456e7004663bc12a74395e637eabcbc IN  0x887a3c3d25eb56953124aede7106c65c8b91720c0 MATIC0.000088742252 1.848065396
0x5460187d6369a4a717b13f3d6c778ef6b9cbadf27917aa0c3d4b50e36f66d482Add Token267286662022-06-13 15:11:28357 days 21 hrs ago0x2c305888a456e7004663bc12a74395e637eabcbc IN  0x887a3c3d25eb56953124aede7106c65c8b91720c0 MATIC0.000108548323 1.868848428
0x0c58351df4a388885616e9b628d239799bf53b26358c3469b9a7328697361d85Grant Role267281792022-06-13 14:24:25357 days 21 hrs ago0x2c305888a456e7004663bc12a74395e637eabcbc IN  0x887a3c3d25eb56953124aede7106c65c8b91720c0 MATIC0.000092476801 1.800000022
0x3db8ce2b7cc72957a3e8a66744a5fc08bb3b1291ee7c1557f6e6103f0ff53f3dGrant Role267281792022-06-13 14:24:25357 days 21 hrs ago0x2c305888a456e7004663bc12a74395e637eabcbc IN  0x887a3c3d25eb56953124aede7106c65c8b91720c0 MATIC0.000092476801 1.800000022
0x1f5175faebdcb907bf34e4778c4920a7a75b75cef160038714541f5ba136103aSet Balance Over...265859232022-06-03 15:39:16367 days 20 hrs ago0x2c305888a456e7004663bc12a74395e637eabcbc IN  0x887a3c3d25eb56953124aede7106c65c8b91720c0 MATIC0.0000741285 1.500000009
0x9de1b8b134a11a8fd015b694085712afc5732df0afbf19f22c62f5077b3f38b8Set House Edge S...265859232022-06-03 15:39:16367 days 20 hrs ago0x2c305888a456e7004663bc12a74395e637eabcbc IN  0x887a3c3d25eb56953124aede7106c65c8b91720c0 MATIC0.000077997 1.500000009
0x00d1e933804c568c59669ae7fbdea0ba573a4bc7ed5976071bbf61ec97bd00e8Grant Role265858262022-06-03 15:28:44367 days 20 hrs ago0x2c305888a456e7004663bc12a74395e637eabcbc IN  0x887a3c3d25eb56953124aede7106c65c8b91720c0 MATIC0.000077064 1.500000013
0x70d8f0c2e84ab026e760660d82eb68118fe38161eabedf1d132037d3865e97a6Grant Role265858262022-06-03 15:28:44367 days 20 hrs ago0x2c305888a456e7004663bc12a74395e637eabcbc IN  0x887a3c3d25eb56953124aede7106c65c8b91720c0 MATIC0.000077064 1.500000013
0x4300f10aba6c4bbb4636b304aae0bf1b6a4aa84f9b0a4bd85070545c520e9e8aDeposit265848742022-06-03 13:48:29367 days 22 hrs ago0x2c305888a456e7004663bc12a74395e637eabcbc IN  0x887a3c3d25eb56953124aede7106c65c8b91720c0 MATIC0.002629650002 25.000000026
0xa7b3a183c7dd3acf5d90953d3e9c7ffe0b19ebb3087bbc0057f234341d75302eDeposit265848682022-06-03 13:47:29367 days 22 hrs ago0x2c305888a456e7004663bc12a74395e637eabcbc IN  0x887a3c3d25eb56953124aede7106c65c8b91720c90 MATIC0.000110013068 1.638708679
0x7bf39d500457b1b54e6eaf3346bdeba386185cfc1a7f679df7f66e3165f2958bSet Balance Risk265847822022-06-03 13:37:13367 days 22 hrs ago0x2c305888a456e7004663bc12a74395e637eabcbc IN  0x887a3c3d25eb56953124aede7106c65c8b91720c0 MATIC0.00007645715 2.450000015
0xe949cce62362b00f86c7c96da6a561bedaa04693e44b6d726fae2879c0ef5cf6Set Allowed Toke...265847822022-06-03 13:37:13367 days 22 hrs ago0x2c305888a456e7004663bc12a74395e637eabcbc IN  0x887a3c3d25eb56953124aede7106c65c8b91720c0 MATIC0.00011764655 2.450000015
0xca491df9fc2fcd6057f242199ac1b53d98562225c856586f453882e61e61f6ccAdd Token265847822022-06-03 13:37:13367 days 22 hrs ago0x2c305888a456e7004663bc12a74395e637eabcbc IN  0x887a3c3d25eb56953124aede7106c65c8b91720c0 MATIC0.0001362445 2.450000015
0x05413c50378924a410932c64896e06ed872e73a70c51cea9ab4e49d911251397Set Balance Over...265847482022-06-03 13:34:22367 days 22 hrs ago0x2c305888a456e7004663bc12a74395e637eabcbc IN  0x887a3c3d25eb56953124aede7106c65c8b91720c0 MATIC0.001229475001 25.000000023
0xb9fb01d8a5d8d17eeb2f43f7cff64e55fd2139181e1c99ed6446a88784b01ca9Set House Edge S...265847472022-06-03 13:34:17367 days 22 hrs ago0x2c305888a456e7004663bc12a74395e637eabcbc IN  0x887a3c3d25eb56953124aede7106c65c8b91720c0 MATIC0.001040335799 20.099999997
0xf9a0f91abc5d1a188dbf2cfd089b38c5efa4d8bf83c6ad7e55b27db763221557Set Balance Risk265847382022-06-03 13:33:32367 days 22 hrs ago0x2c305888a456e7004663bc12a74395e637eabcbc IN  0x887a3c3d25eb56953124aede7106c65c8b91720c0 MATIC0.000069213574 2.235075233
0x7b3833a0d8c3e5bd35a6912b2def52f1c5b0c7d274268c96ea28a459dc1b706dSet Allowed Toke...265847372022-06-03 13:33:27367 days 22 hrs ago0x2c305888a456e7004663bc12a74395e637eabcbc IN  0x887a3c3d25eb56953124aede7106c65c8b91720c0 MATIC0.000107858161 2.257438658
0xfbfab6e986177f514546a68873e060e63fc88ffe34397e84d0f70f7a7e5b2816Add Token265847362022-06-03 13:33:22367 days 22 hrs ago0x2c305888a456e7004663bc12a74395e637eabcbc IN  0x887a3c3d25eb56953124aede7106c65c8b91720c0 MATIC0.001248675001 25.000000035
0x71b3f31f69fc4a2f7a793f34bdff8a94c446f5d80556850404d8418b55c9eab7Grant Role265821042022-06-03 8:54:41368 days 3 hrs ago0x2c305888a456e7004663bc12a74395e637eabcbc IN  0x887a3c3d25eb56953124aede7106c65c8b91720c0 MATIC0.00157112553 30.580923595
[ Download CSV Export 
Latest 25 internal transaction
Parent Txn Hash Block From To Value
0x43fb332e7b5d13b9d968f366fb3cbdbcf93777e4dca104b7cccbfab896cf51f5277868252022-08-26 14:00:21283 days 22 hrs ago 0x887a3c3d25eb56953124aede7106c65c8b91720c0x2c305888a456e7004663bc12a74395e637eabcbc91.251810767310589772 MATIC
0x3b73f2efcf6b1b234d0d6ae2885557e9cf08517c3c74c024d15b4073a17c2bde277868252022-08-26 14:00:21283 days 22 hrs ago 0x887a3c3d25eb56953124aede7106c65c8b91720c 0x8343f2dc53e55dbbe626b9d8b4fbbb4e37dc218b0.046290824210893842 MATIC
0x3b73f2efcf6b1b234d0d6ae2885557e9cf08517c3c74c024d15b4073a17c2bde277868252022-08-26 14:00:21283 days 22 hrs ago 0x887a3c3d25eb56953124aede7106c65c8b91720c0x2c305888a456e7004663bc12a74395e637eabcbc0.067660435385152549 MATIC
0x73d5a8deaf32f9905da924efac5592737082d015c3e27d3426c6562645bcf29f277184912022-08-21 17:10:58288 days 19 hrs ago 0x887a3c3d25eb56953124aede7106c65c8b91720c0xbc89cae970423adf516d2bdd6e50d67847ca93c60.0097 MATIC
0x73d5a8deaf32f9905da924efac5592737082d015c3e27d3426c6562645bcf29f277184912022-08-21 17:10:58288 days 19 hrs ago 0x887a3c3d25eb56953124aede7106c65c8b91720c 0xa368c0ab32dd9bc0500075c0c8397b6b0168e4320.000018 MATIC
0x73d5a8deaf32f9905da924efac5592737082d015c3e27d3426c6562645bcf29f277184912022-08-21 17:10:58288 days 19 hrs ago 0x52a6b58e3dfad12be315a3f4f34897f46c40c06c 0x887a3c3d25eb56953124aede7106c65c8b91720c0.0003 MATIC
0x2a3d945236340bd86f3c138802aaa0e70c7e702d1f09541d3d7bb69d655ecda3277184742022-08-21 17:08:08288 days 19 hrs ago 0x887a3c3d25eb56953124aede7106c65c8b91720c0xbc89cae970423adf516d2bdd6e50d67847ca93c60.003233333333333334 MATIC
0x2a3d945236340bd86f3c138802aaa0e70c7e702d1f09541d3d7bb69d655ecda3277184742022-08-21 17:08:08288 days 19 hrs ago 0x887a3c3d25eb56953124aede7106c65c8b91720c 0xa368c0ab32dd9bc0500075c0c8397b6b0168e4320.000011999999999999 MATIC
0x2a3d945236340bd86f3c138802aaa0e70c7e702d1f09541d3d7bb69d655ecda3277184742022-08-21 17:08:08288 days 19 hrs ago 0x688f9f9a662751ddee5e38ece496d341a3ea49e4 0x887a3c3d25eb56953124aede7106c65c8b91720c0.0003 MATIC
0x48c358002003610de4bf2924b7e4158c93db3df0d74bc3a131860e6c5ab81784275948862022-08-12 23:06:35297 days 13 hrs ago 0x688f9f9a662751ddee5e38ece496d341a3ea49e4 0x887a3c3d25eb56953124aede7106c65c8b91720c0.1 MATIC
0xcd8ab2220c449be1f3a8fa6f689780eaaf44d56738d9259edb7f4042a1d494ed275899572022-08-12 14:59:53297 days 21 hrs ago 0x887a3c3d25eb56953124aede7106c65c8b91720c0xef8a64e15400140bee3bab69ccd163ff3c2532ed0.097 MATIC
0xcd8ab2220c449be1f3a8fa6f689780eaaf44d56738d9259edb7f4042a1d494ed275899572022-08-12 14:59:53297 days 21 hrs ago 0x52a6b58e3dfad12be315a3f4f34897f46c40c06c 0x887a3c3d25eb56953124aede7106c65c8b91720c0.003 MATIC
0x60c2e77a6408804cbfb661a4226cdc2e3fe2bf52ea799ba52f5f4bf2bfb3b88a275899302022-08-12 14:57:37297 days 21 hrs ago 0x52a6b58e3dfad12be315a3f4f34897f46c40c06c 0x887a3c3d25eb56953124aede7106c65c8b91720c0.1 MATIC
0x446031aa0981b7777842a06356d8a2bce7bdfc93b5458fc09e53243071f1b9f8275898762022-08-12 14:53:06297 days 21 hrs ago 0x887a3c3d25eb56953124aede7106c65c8b91720c0xef8a64e15400140bee3bab69ccd163ff3c2532ed0.202708333333333334 MATIC
0x446031aa0981b7777842a06356d8a2bce7bdfc93b5458fc09e53243071f1b9f8275898762022-08-12 14:53:06297 days 21 hrs ago 0x8911f76efb1fb040d2e01d22d55980d7ec4879d6 0x887a3c3d25eb56953124aede7106c65c8b91720c0.0027 MATIC
0xe050184acecde68c5f1b76287b18a040a9aa9832ab5504e3c8a467ea7438c2a0275815732022-08-12 1:11:02298 days 11 hrs ago 0x887a3c3d25eb56953124aede7106c65c8b91720c0xef8a64e15400140bee3bab69ccd163ff3c2532ed0.097 MATIC
0xe050184acecde68c5f1b76287b18a040a9aa9832ab5504e3c8a467ea7438c2a0275815732022-08-12 1:11:02298 days 11 hrs ago 0x52a6b58e3dfad12be315a3f4f34897f46c40c06c 0x887a3c3d25eb56953124aede7106c65c8b91720c0.003 MATIC
0xdc1046a3d3666046e21f7cce3b86d7db51003f11a22db23166e63bf20538e424275815732022-08-12 1:11:02298 days 11 hrs ago 0x887a3c3d25eb56953124aede7106c65c8b91720c0xef8a64e15400140bee3bab69ccd163ff3c2532ed0.102705555555555556 MATIC
0xdc1046a3d3666046e21f7cce3b86d7db51003f11a22db23166e63bf20538e424275815732022-08-12 1:11:02298 days 11 hrs ago 0x8911f76efb1fb040d2e01d22d55980d7ec4879d6 0x887a3c3d25eb56953124aede7106c65c8b91720c0.0027 MATIC
0x74848aa032411ee1569eb7a551e26212f444700a2ecad51717de72cd24895801274851932022-08-05 16:15:00304 days 20 hrs ago 0x52a6b58e3dfad12be315a3f4f34897f46c40c06c 0x887a3c3d25eb56953124aede7106c65c8b91720c0.1 MATIC
0xfecf511185ddb33857432f5212425741568c0d4e77b9a0d7966fc0ee1ab15ac4274851842022-08-05 16:13:30304 days 20 hrs ago 0x887a3c3d25eb56953124aede7106c65c8b91720c0x1a8a92665e02c9b78585cc812b25af901d4672fa0.052704166666666667 MATIC
0xfecf511185ddb33857432f5212425741568c0d4e77b9a0d7966fc0ee1ab15ac4274851842022-08-05 16:13:30304 days 20 hrs ago 0x8911f76efb1fb040d2e01d22d55980d7ec4879d6 0x887a3c3d25eb56953124aede7106c65c8b91720c0.0027 MATIC
0x2043e808d92c7771f5453b83bae4ee14327e7c6829bec3c0736e3f23fb0d3bc9274851552022-08-05 16:10:59304 days 20 hrs ago 0x8911f76efb1fb040d2e01d22d55980d7ec4879d6 0x887a3c3d25eb56953124aede7106c65c8b91720c0.1 MATIC
0x06182af7d82ec3009a2ede86203fcc567826f2d8d4a6d861cc865cfe7383e365274850382022-08-05 16:01:12304 days 20 hrs ago 0x52a6b58e3dfad12be315a3f4f34897f46c40c06c 0x887a3c3d25eb56953124aede7106c65c8b91720c0.2 MATIC
0xb8e809c38fdf62bbbe2e7fc69f85f98caac88fe3029ff8316b29f5ec25b5e051274850262022-08-05 16:00:12304 days 20 hrs ago 0x887a3c3d25eb56953124aede7106c65c8b91720c0x1a8a92665e02c9b78585cc812b25af901d4672fa0.102705555555555556 MATIC
[ Download CSV Export 
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
Bank

Compiler Version
v0.8.14+commit.80d49f37

Optimization Enabled:
Yes with 8000 runs

Other Settings:
default evmVersion, MIT license
File 1 of 15 : 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 15 : 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 15 : 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 15 : AccessControl.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.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, _msgSender());
        _;
    }

    /**
     * @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 `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.
     */
    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.
     */
    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`.
     */
    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.
     *
     * [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.
     */
    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.
     */
    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 15 : 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 6 of 15 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

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

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

    /**
     * @dev Moves `amount` tokens from the caller's account to `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);

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

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

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

pragma solidity ^0.8.0;

import "../IERC20.sol";

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

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

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

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

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

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

File 9 of 15 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.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

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

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

pragma solidity ^0.8.0;

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

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

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

pragma solidity ^0.8.0;

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

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

File 12 of 15 : 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 13 of 15 : 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 14 of 15 : Bank.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.10;

import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "@chainlink/contracts/src/v0.8/KeeperCompatible.sol";

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

// 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),
/// manage the tokens balance overflow to dynamically send overflowed tokens to the treasury and team.
/// 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 AccessControl, KeeperCompatibleInterface {
    using SafeERC20 for IERC20;

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

    /// @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 dividend Rate to be allocated as staking rewards, on bet payout.
    /// @param referral Rate to be allocated to the referrers, 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.
    /// @param referralAmount The number of tokens to be sent to the referral program contract.
    /// @param minPartnerTransferAmount The minimum amount of token to distribute the partner house edge.
    struct HouseEdgeSplit {
        uint16 dividend;
        uint16 referral;
        uint16 partner;
        uint16 treasury;
        uint16 team;
        uint256 dividendAmount;
        uint256 partnerAmount;
        uint256 treasuryAmount;
        uint256 teamAmount;
        uint256 referralAmount;
        uint256 minPartnerTransferAmount;
    }

    /// @notice Token's balance overflow struct.
    /// @param thresholdRate Threshold rate for the token's balance reference.
    /// @param toTreasury Rate to be allocated to the treasury.
    /// @param toTeam Rate to be allocated to the team.
    struct BalanceOverflow {
        uint16 thresholdRate;
        uint16 toTreasury;
        uint16 toTeam;
    }

    /// @notice Token struct.
    /// List of tokens to bet on games.
    /// @param allowed Whether the token is allowed for bets.
    /// @param balanceRisk Defines the maximum bank payout, used to calculate the max bet amount.
    /// @param partner Address of the partner to manage the token and receive the house edge.
    /// @param houseEdgeSplit House edge allocations.
    /// @param balanceReference Balance reference used to manage the bank overflow.
    /// @param balanceOverflow Balance overflow management configuration.
    struct Token {
        bool allowed;
        uint16 balanceRisk;
        address partner;
        HouseEdgeSplit houseEdgeSplit;
        uint256 balanceReference;
        BalanceOverflow balanceOverflow;
    }

    /// @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.
    uint16 public tokensCount;

    /// @notice Chainlink Keeper Registry address.
    address public keeperRegistry;

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

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

    /// @notice Referral program contract.
    IReferral public referralProgram;

    /// @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(uint16 => address) public tokensList;

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

    /// @notice Emitted after the referral program is set.
    /// @param referralProgram The referral program address.
    event SetReferralProgram(address referralProgram);

    /// @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 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 Chainlink Keeper Registry is set.
    /// @param keeperRegistry Address of the Keeper Registry.
    event SetKeeperRegistry(address keeperRegistry);

    /// @notice Emitted after the token's house edge allocations for bet payout is set.
    /// @param token Address of the token.
    /// @param dividend Rate to be allocated as staking rewards, on bet payout.
    /// @param referral Rate to be allocated to the referrers, 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 dividend,
        uint16 referral,
        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 referral allocation is distributed.
    /// @param token Address of the token.
    /// @param referralProgram The address of the Referral Program contract.
    /// @param referralAmount The number of tokens sent.
    event DistributeReferralAmount(
        address indexed token,
        address referralProgram,
        uint256 referralAmount
    );

    /// @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 balance overflow management configuration is set.
    /// @param token Address of the token.
    /// @param thresholdRate Threshold rate for the token's balance reference.
    /// @param toTreasury Rate to be allocated to the treasury.
    /// @param toTeam Rate to be allocated to the team.
    event SetBalanceOverflow(
        address indexed token,
        uint16 thresholdRate,
        uint16 toTreasury,
        uint16 toTeam
    );

    /// @notice Emitted after the token's bank overflow amount is distributed to the treasury and team.
    /// @param token Address of the token.
    /// @param amountToTreasury The number of tokens sent to the treasury.
    /// @param amountToTeam The number of tokens sent to the team.
    event BankOverflowTransfer(
        address indexed token,
        uint256 amountToTreasury,
        uint256 amountToTeam
    );

    /// @notice Emitted after the token's balance reference is set.
    /// This happends on deposit, withdraw and when the bank overflow threashold is reached.
    /// @param token Address of the token.
    /// @param balanceReference New balance reference used to determine the bank overflow.
    event SetBalanceReference(address indexed token, uint256 balanceReference);

    /// @notice Emitted after the token's house edge is allocated.
    /// @param token Address of the token.
    /// @param dividend The number of tokens allocated as staking rewards.
    /// @param referral The number of tokens allocated to the referrers.
    /// @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 dividend,
        uint256 referral,
        uint256 partner,
        uint256 treasury,
        uint256 team
    );

    /// @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.
    /// @param token Address of the token.
    error TokenExists(address token);
    /// @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 setting wrong balance overflow management configuration.
    error WrongBalanceOverflow();
    /// @notice Reverting error when sender isn't allowed.
    error AccessDenied();
    /// @notice Reverting error when referral program or team wallet is the zero address.
    error WrongAddress();

    /// @notice Modifier that checks that an account is allowed to interact.
    /// @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.
    /// @param referralProgramAddress The referral program.
    constructor(
        address payable treasuryAddress,
        address payable teamWalletAddress,
        IReferral referralProgramAddress
    ) {
        // The ownership should then be transfered to the Timelock.
        _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);

        treasury = treasuryAddress;
        setTeamWallet(teamWalletAddress);
        setReferralProgram(referralProgramAddress);
    }

    /// @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 payable user,
        address token,
        uint256 amount
    ) private {
        if (_isGasToken(token)) {
            Address.sendValue(user, amount);
        } else {
            IERC20(token).safeTransfer(user, amount);
        }
    }

    /// @notice Sets the new token's balance reference.
    /// @param token Address of the token.
    /// @param newReference Balance amount corresponding to the new reference.
    function _setBalanceReference(address token, uint256 newReference) private {
        tokens[token].balanceReference = newReference;
        emit SetBalanceReference(token, newReference);
    }

    /// @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.
    /// It is also setting the new balance reference, used to manage the bank overflow.
    /// 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)
    {
        uint256 balance = getBalance(token);
        if (_isGasToken(token)) {
            _setBalanceReference(token, balance);
            amount = msg.value;
        } else {
            _setBalanceReference(token, balance + amount);
            IERC20(token).safeTransferFrom(msg.sender, address(this), amount);
        }
        emit Deposit(token, amount);
    }

    /// @notice Withdraw funds from the bank to migrate.
    /// It is also setting the new balance reference, used to manage the bank overflow.
    /// @param token Address of the token.
    /// @param amount Number of tokens.
    function withdraw(address token, uint256 amount)
        external
        onlyTokenOwner(DEFAULT_ADMIN_ROLE, token)
    {
        _setBalanceReference(token, getBalance(token) - amount);
        _safeTransfer(payable(msg.sender), token, amount);
        emit Withdraw(token, amount);
    }

    /// @notice Sets the keeper registry address
    /// @param keeperRegistryAddress Chainlink Keeper Registry.
    function setKeeperRegistry(address keeperRegistryAddress)
        external
        onlyRole(DEFAULT_ADMIN_ROLE)
    {
        if (keeperRegistryAddress != keeperRegistry) {
            keeperRegistry = keeperRegistryAddress;
            emit SetKeeperRegistry(keeperRegistryAddress);
        }
    }

    /// @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 (uint16 i; i < tokensCount; i++) {
                if (tokensList[i] == token) {
                    revert TokenExists(token);
                }
            }
        }
        tokensList[tokensCount] = token;
        tokensCount += 1;
        emit AddToken(token);
    }

    /// @notice Changes the token's bet permission on an already added token.
    /// @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 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]
            .houseEdgeSplit
            .minPartnerTransferAmount = minPartnerTransferAmount;
        emit SetMinPartnerTransferAmount(token, minPartnerTransferAmount);
    }

    /// @notice Changes the token's partner address.
    /// @param token Address of the token.
    /// @param partner Address of the partner.
    function setTokenPartner(address token, address partner)
        external
        onlyTokenOwner(DEFAULT_ADMIN_ROLE, 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 dividend Rate to be allocated as staking rewards, on bet payout.
    /// @param referral Rate to be allocated to the referrers, 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 `dividend`, `referral`, `_treasury` and `team` rates sum must equals 10000.
    function setHouseEdgeSplit(
        address token,
        uint16 dividend,
        uint16 referral,
        uint16 partner,
        uint16 _treasury,
        uint16 team
    ) external onlyRole(DEFAULT_ADMIN_ROLE) {
        uint16 splitSum = dividend + team + partner + _treasury + referral;
        if (splitSum != 10000) {
            revert WrongHouseEdgeSplit(splitSum);
        }

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

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

    /// @notice Sets the token's balance overflow management configuration.
    /// The threshold shouldn't exceed 100% to be able to calculate the overflowed amount.
    /// The treasury and team rates sum shouldn't exceed 100% to allow the bank balance to grow organically.
    /// @param token Address of the token.
    /// @param thresholdRate Threshold rate for the token's balance reference.
    /// @param toTreasury Rate to be allocated to the treasury.
    /// @param toTeam Rate to be allocated to the team.
    function setBalanceOverflow(
        address token,
        uint16 thresholdRate,
        uint16 toTreasury,
        uint16 toTeam
    ) external onlyRole(DEFAULT_ADMIN_ROLE) {
        if (thresholdRate > 10000 || (toTreasury + toTeam) > 10000) {
            revert WrongBalanceOverflow();
        }

        tokens[token].balanceOverflow = BalanceOverflow(
            thresholdRate,
            toTreasury,
            toTeam
        );
        emit SetBalanceOverflow(token, thresholdRate, toTreasury, toTeam);
    }

    /// @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 (uint16 i; i < tokensCount; i++) {
            address tokenAddress = tokensList[i];
            Token storage token = tokens[tokenAddress];
            uint256 dividendAmount = token.houseEdgeSplit.dividendAmount;
            if (dividendAmount != 0) {
                token.houseEdgeSplit.dividendAmount = 0;
                _safeTransfer(
                    payable(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 (uint16 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 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) {
        // Splits the house edge fees and allocates them as dividends, for referrers, to the partner, the treasury, and team.
        // If the user has no referrer, the referral allocation is allocated evenly among the other allocations.
        {
            HouseEdgeSplit storage tokenHouseEdge = tokens[token]
                .houseEdgeSplit;

            // Calculate the referral allocation
            uint256 referralAllocation = (fees * tokenHouseEdge.referral) /
                10000;
            uint256 referralAmount;
            if (referralAllocation != 0) {
                referralAmount = referralProgram.payReferral(
                    user,
                    token,
                    referralAllocation
                );
                referralAllocation -= referralAmount;
            }
            uint256 dividendAmount = (fees * tokenHouseEdge.dividend) / 10000;
            uint256 partnerAmount = (fees * tokenHouseEdge.partner) / 10000;
            uint256 treasuryAmount = (fees * tokenHouseEdge.treasury) / 10000;
            uint256 teamAmount = (fees * tokenHouseEdge.team) / 10000;

            uint8 allocationsCount;
            if (dividendAmount != 0) {
                allocationsCount++;
            }
            if (partnerAmount != 0) {
                allocationsCount++;
            }
            if (treasuryAmount != 0) {
                allocationsCount++;
            }
            if (teamAmount != 0) {
                allocationsCount++;
            }

            uint256 referralAllocationRestPerSplit;
            if (allocationsCount != 0) {
                referralAllocationRestPerSplit =
                    (referralAllocation -
                        (referralAllocation % allocationsCount)) /
                    allocationsCount;
            }

            if (dividendAmount != 0) {
                dividendAmount += referralAllocationRestPerSplit;
                tokenHouseEdge.dividendAmount += dividendAmount;
            }
            if (partnerAmount != 0) {
                partnerAmount += referralAllocationRestPerSplit;
                tokenHouseEdge.partnerAmount += partnerAmount;
            }
            if (treasuryAmount != 0) {
                treasuryAmount += referralAllocationRestPerSplit;
                tokenHouseEdge.treasuryAmount += treasuryAmount;
            }
            if (teamAmount != 0) {
                teamAmount += referralAllocationRestPerSplit;
                tokenHouseEdge.teamAmount += teamAmount;
            }

            if (referralAmount != 0) {
                // If no registered Chainlink Keepers, transfer to the referral program.
                if (keeperRegistry == address(0)) {
                    _safeTransfer(
                        payable(address(referralProgram)),
                        token,
                        referralAmount
                    );
                } else {
                    tokenHouseEdge.referralAmount += referralAmount;
                }
            }

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

        // 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 {
        if (msg.sender != keeperRegistry) {
            revert AccessDenied();
        }
        (UpkeepActions upkeepAction, address tokenAddress) = abi.decode(
            performData,
            (UpkeepActions, address)
        );
        HouseEdgeSplit memory houseEdgeSplit = tokens[tokenAddress]
            .houseEdgeSplit;

        if (upkeepAction == UpkeepActions.ManageBalanceOverflow) {
            manageBalanceOverflow(tokenAddress);
        } else if (
            upkeepAction == UpkeepActions.DistributePartnerHouseEdge &&
            houseEdgeSplit.partnerAmount >
            houseEdgeSplit.minPartnerTransferAmount
        ) {
            withdrawPartnerAmount(tokenAddress);
        } else if (
            upkeepAction == UpkeepActions.DistributeReferralHouseEdge &&
            houseEdgeSplit.referralAmount > 0
        ) {
            withdrawReferralAmount(tokenAddress);
        }
    }

    /// @dev For the front-end
    function getTokens() external view returns (TokenMetadata[] memory) {
        TokenMetadata[] memory _tokens = new TokenMetadata[](tokensCount);
        for (uint16 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 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 token Address of the token.
    /// @return Whether the token is enabled for bets.
    function isAllowedToken(address token) external view returns (bool) {
        return tokens[token].allowed;
    }

    /// @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) {
            HouseEdgeSplit memory houseEdgeSplit = tokens[tokenAddressData]
                .houseEdgeSplit;
            if (
                houseEdgeSplit.partnerAmount >
                houseEdgeSplit.minPartnerTransferAmount
            ) {
                upkeepNeeded = true;
                performData = abi.encode(upkeepAction, tokenAddressData);
            }
        } else {
            for (uint16 i; i < tokensCount; i++) {
                address tokenAddress = tokensList[i];
                Token memory token = tokens[tokenAddress];
                HouseEdgeSplit memory houseEdgeSplit = token.houseEdgeSplit;
                if (
                    upkeepAction == UpkeepActions.ManageBalanceOverflow &&
                    token.partner == address(0)
                ) {
                    uint256 tokenBalance = getBalance(tokenAddress);
                    uint256 overflow = (token.balanceReference +
                        ((tokenBalance * token.balanceOverflow.thresholdRate) /
                            10000));
                    if (tokenBalance > overflow) {
                        upkeepNeeded = true;
                        performData = abi.encode(upkeepAction, tokenAddress);
                        break;
                    }
                } else if (
                    upkeepAction == UpkeepActions.DistributeReferralHouseEdge &&
                    houseEdgeSplit.referralAmount > 0
                ) {
                    upkeepNeeded = true;
                    performData = abi.encode(upkeepAction, tokenAddress);
                }
            }
        }
    }

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

    /// @notice Sets the new referral program.
    /// @param _referralProgram The referral program address.
    function setReferralProgram(IReferral _referralProgram)
        public
        onlyRole(DEFAULT_ADMIN_ROLE)
    {
        if (address(_referralProgram) == address(0)) {
            revert WrongAddress();
        }
        referralProgram = _referralProgram;
        emit SetReferralProgram(address(referralProgram));
    }

    /// @notice Manages the balance overflow.
    /// @notice When the bank overflow threshold amount is reached on a token balance,
    /// the bank sends a percentage to the treasury and team, and the new token's balance reference is set.
    /// @param tokenAddress Address of the token.
    function manageBalanceOverflow(address tokenAddress) public {
        Token storage token = tokens[tokenAddress];
        uint256 tokenBalance = getBalance(tokenAddress);
        uint256 overflow = (token.balanceReference +
            ((tokenBalance * token.balanceOverflow.thresholdRate) / 10000));
        if (token.partner == address(0) && tokenBalance > overflow) {
            uint256 diff = tokenBalance - token.balanceReference;
            uint256 overflowAmountToTreasury = ((diff *
                token.balanceOverflow.toTreasury) / 10000);
            uint256 overflowAmountToTeam = ((diff *
                token.balanceOverflow.toTeam) / 10000);
            _setBalanceReference(
                tokenAddress,
                tokenBalance - overflowAmountToTreasury - overflowAmountToTeam
            );

            uint256 treasuryAmount = token.houseEdgeSplit.treasuryAmount;
            uint256 teamAmount = token.houseEdgeSplit.teamAmount;
            token.houseEdgeSplit.treasuryAmount = 0;
            token.houseEdgeSplit.teamAmount = 0;

            _safeTransfer(
                treasury,
                tokenAddress,
                treasuryAmount + overflowAmountToTreasury
            );
            _safeTransfer(
                teamWallet,
                tokenAddress,
                teamAmount + overflowAmountToTeam
            );
            emit BankOverflowTransfer(
                tokenAddress,
                treasuryAmount + overflowAmountToTreasury,
                teamAmount + overflowAmountToTeam
            );
        }
    }

    /// @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) {
            tokenHouseEdge.treasuryAmount = 0;
            _safeTransfer(treasury, tokenAddress, treasuryAmount);
        }
        if (teamAmount != 0) {
            tokenHouseEdge.teamAmount = 0;
            _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)) {
            token.houseEdgeSplit.partnerAmount = 0;
            _safeTransfer(payable(token.partner), tokenAddress, partnerAmount);
            emit HouseEdgePartnerDistribution(tokenAddress, partnerAmount);
        }
    }

    /// @notice Distributes the token's referral amount.
    /// @param tokenAddress Address of the token.
    function withdrawReferralAmount(address tokenAddress) public {
        HouseEdgeSplit storage tokenHouseEdge = tokens[tokenAddress]
            .houseEdgeSplit;
        uint256 referralAmount = tokenHouseEdge.referralAmount;
        if (referralAmount != 0) {
            address referralProgramAddress = address(referralProgram);
            tokenHouseEdge.referralAmount = 0;
            _safeTransfer(
                payable(referralProgramAddress),
                tokenAddress,
                referralAmount
            );
            emit DistributeReferralAmount(
                tokenAddress,
                referralProgramAddress,
                referralAmount
            );
        }
    }

    /// @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 -
            tokenHouseEdgeSplit.referralAmount;
    }
}

File 15 of 15 : IReferral.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.10;

/// @notice Referral interface
/// @author Romuald Hog.
interface IReferral {
    /// @notice Adds an address as referrer.
    /// @param user The address of the user.
    /// @param referrer The address would set as referrer of user.
    function addReferrer(address user, address referrer) external;

    /// @notice Updates referrer's last active timestamp.
    /// @param user The address would like to update active time.
    function updateReferrerActivity(address user) external;

    /// @notice Calculates and allocate referrer(s) credits to uplines.
    /// @param user Address of the gamer to find referrer(s).
    /// @param token The token to allocate.
    /// @param amount The number of tokens allocated for referrer(s).
    function payReferral(
        address user,
        address token,
        uint256 amount
    ) external returns (uint256);

    /// @notice Utils function for check whether an address has the referrer.
    /// @param user The address of the user.
    /// @return Whether user has a referrer.
    function hasReferrer(address user) external view returns (bool);
}

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 payable","name":"treasuryAddress","type":"address"},{"internalType":"address payable","name":"teamWalletAddress","type":"address"},{"internalType":"contract IReferral","name":"referralProgramAddress","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AccessDenied","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"TokenExists","type":"error"},{"inputs":[],"name":"WrongAddress","type":"error"},{"inputs":[],"name":"WrongBalanceOverflow","type":"error"},{"inputs":[{"internalType":"uint16","name":"splitSum","type":"uint16"}],"name":"WrongHouseEdgeSplit","type":"error"},{"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":"dividend","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"referral","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":"amountToTreasury","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountToTeam","type":"uint256"}],"name":"BankOverflowTransfer","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":"address","name":"referralProgram","type":"address"},{"indexed":false,"internalType":"uint256","name":"referralAmount","type":"uint256"}],"name":"DistributeReferralAmount","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":"thresholdRate","type":"uint16"},{"indexed":false,"internalType":"uint16","name":"toTreasury","type":"uint16"},{"indexed":false,"internalType":"uint16","name":"toTeam","type":"uint16"}],"name":"SetBalanceOverflow","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"balanceReference","type":"uint256"}],"name":"SetBalanceReference","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":false,"internalType":"address","name":"keeperRegistry","type":"address"}],"name":"SetKeeperRegistry","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":false,"internalType":"address","name":"referralProgram","type":"address"}],"name":"SetReferralProgram","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":"dividend","type":"uint16"},{"indexed":false,"internalType":"uint16","name":"referral","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":"address","name":"partner","type":"address"}],"name":"SetTokenPartner","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"}],"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":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"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":"uint16","name":"balanceRisk","type":"uint16"},{"internalType":"address","name":"partner","type":"address"},{"components":[{"internalType":"uint16","name":"dividend","type":"uint16"},{"internalType":"uint16","name":"referral","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":"uint256","name":"referralAmount","type":"uint256"},{"internalType":"uint256","name":"minPartnerTransferAmount","type":"uint256"}],"internalType":"struct Bank.HouseEdgeSplit","name":"houseEdgeSplit","type":"tuple"},{"internalType":"uint256","name":"balanceReference","type":"uint256"},{"components":[{"internalType":"uint16","name":"thresholdRate","type":"uint16"},{"internalType":"uint16","name":"toTreasury","type":"uint16"},{"internalType":"uint16","name":"toTeam","type":"uint16"}],"internalType":"struct Bank.BalanceOverflow","name":"balanceOverflow","type":"tuple"}],"internalType":"struct Bank.Token","name":"token","type":"tuple"}],"internalType":"struct Bank.TokenMetadata[]","name":"","type":"tuple[]"}],"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":"token","type":"address"}],"name":"isAllowedToken","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"keeperRegistry","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"}],"name":"manageBalanceOverflow","outputs":[],"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":[],"name":"referralProgram","outputs":[{"internalType":"contract IReferral","name":"","type":"address"}],"stateMutability":"view","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":"thresholdRate","type":"uint16"},{"internalType":"uint16","name":"toTreasury","type":"uint16"},{"internalType":"uint16","name":"toTeam","type":"uint16"}],"name":"setBalanceOverflow","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":"dividend","type":"uint16"},{"internalType":"uint16","name":"referral","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":"keeperRegistryAddress","type":"address"}],"name":"setKeeperRegistry","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":"contract IReferral","name":"_referralProgram","type":"address"}],"name":"setReferralProgram","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address payable","name":"_teamWallet","type":"address"}],"name":"setTeamWallet","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":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"teamWallet","outputs":[{"internalType":"address payable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"tokens","outputs":[{"internalType":"bool","name":"allowed","type":"bool"},{"internalType":"uint16","name":"balanceRisk","type":"uint16"},{"internalType":"address","name":"partner","type":"address"},{"components":[{"internalType":"uint16","name":"dividend","type":"uint16"},{"internalType":"uint16","name":"referral","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":"uint256","name":"referralAmount","type":"uint256"},{"internalType":"uint256","name":"minPartnerTransferAmount","type":"uint256"}],"internalType":"struct Bank.HouseEdgeSplit","name":"houseEdgeSplit","type":"tuple"},{"internalType":"uint256","name":"balanceReference","type":"uint256"},{"components":[{"internalType":"uint16","name":"thresholdRate","type":"uint16"},{"internalType":"uint16","name":"toTreasury","type":"uint16"},{"internalType":"uint16","name":"toTeam","type":"uint16"}],"internalType":"struct Bank.BalanceOverflow","name":"balanceOverflow","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokensCount","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint16","name":"","type":"uint16"}],"name":"tokensList","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"treasury","outputs":[{"internalType":"address payable","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"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"}],"name":"withdrawReferralAmount","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60a06040523480156200001157600080fd5b50604051620049c3380380620049c38339810160408190526200003491620004ab565b620000416000336200006d565b6001600160a01b03831660805262000059826200007d565b620000648162000109565b50505062000679565b6200007982826200018e565b5050565b60006200008b81336200022e565b6001600160a01b038216620000b357604051630d23cf4160e11b815260040160405180910390fd5b600280546001600160a01b0319166001600160a01b0384169081179091556040519081527fc6a5dd316fe9d0339f2769deab7e31f64c8f5b101ffd85dfc9a83dbeaf2e69da906020015b60405180910390a15050565b60006200011781336200022e565b6001600160a01b0382166200013f57604051630d23cf4160e11b815260040160405180910390fd5b600380546001600160a01b0319166001600160a01b0384169081179091556040519081527f678edf136de423322d705b75c3157ecfde496e1ad82aef725aa6deb9d94d4de290602001620000fd565b6000828152602081815260408083206001600160a01b038516845290915290205460ff1662000079576000828152602081815260408083206001600160a01b03851684529091529020805460ff19166001179055620001ea3390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b6000828152602081815260408083206001600160a01b038516845290915290205460ff16620000795762000278816001600160a01b03166014620002d260201b62002edc1760201c565b6200028e83602062002edc620002d2821b17811c565b604051602001620002a192919062000532565b60408051601f198184030181529082905262461bcd60e51b8252620002c991600401620005ab565b60405180910390fd5b60606000620002e3836002620005f6565b620002f090600262000618565b6001600160401b038111156200030a576200030a62000633565b6040519080825280601f01601f19166020018201604052801562000335576020820181803683370190505b509050600360fc1b8160008151811062000353576200035362000649565b60200101906001600160f81b031916908160001a905350600f60fb1b8160018151811062000385576200038562000649565b60200101906001600160f81b031916908160001a9053506000620003ab846002620005f6565b620003b890600162000618565b90505b60018111156200043a576f181899199a1a9b1b9c1cb0b131b232b360811b85600f1660108110620003f057620003f062000649565b1a60f81b82828151811062000409576200040962000649565b60200101906001600160f81b031916908160001a90535060049490941c9362000432816200065f565b9050620003bb565b5083156200048b5760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401620002c9565b9392505050565b6001600160a01b0381168114620004a857600080fd5b50565b600080600060608486031215620004c157600080fd5b8351620004ce8162000492565b6020850151909350620004e18162000492565b6040850151909250620004f48162000492565b809150509250925092565b60005b838110156200051c57818101518382015260200162000502565b838111156200052c576000848401525b50505050565b7f416363657373436f6e74726f6c3a206163636f756e74200000000000000000008152600083516200056c816017850160208801620004ff565b7001034b99036b4b9b9b4b733903937b6329607d1b60179184019182015283516200059f816028840160208801620004ff565b01602801949350505050565b6020815260008251806020840152620005cc816040850160208701620004ff565b601f01601f19169190910160400192915050565b634e487b7160e01b600052601160045260246000fd5b6000816000190483118215151615620006135762000613620005e0565b500290565b600082198211156200062e576200062e620005e0565b500190565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b600081620006715762000671620005e0565b506000190190565b608051614320620006a36000396000818161054e01528181610ac301526127f601526143206000f3fe6080604052600436106102d15760003560e01c8063802ad8fb11610179578063bc3ec383116100d6578063d547741f1161008a578063f3fef3a311610064578063f3fef3a31461097a578063f6ae22041461099a578063f8b2cb4f146109ce57600080fd5b8063d547741f14610813578063e486033914610833578063ed442e141461096557600080fd5b8063cdc42344116100bb578063cdc423441461079d578063d1bf5768146107bd578063d48bfca7146107f357600080fd5b8063bc3ec38314610744578063cbe230c31461076457600080fd5b8063a217fddf1161012d578063aa6ca80811610112578063aa6ca808146106ce578063ab400706146106f0578063b291b8231461072457600080fd5b8063a217fddf1461068b578063a64ed8ba146106a057600080fd5b80638aaa22841161015e5780638aaa22841461060757806391d1485414610627578063a128c4d21461066b57600080fd5b8063802ad8fb146105be57806383e22774146105e157600080fd5b8063384feaac1161023257806350cacb9f116101e657806361d027b3116101c057806361d027b31461053c578063659bdfc6146105705780636e04ff0d1461059057600080fd5b806350cacb9f146104c457806356f0406d146104e4578063599270441461050457600080fd5b80633e32a381116102175780633e32a381146104715780634585e33b1461049157806347e7ef24146104b157600080fd5b8063384feaac1461043157806338c64d2f1461045157600080fd5b8063278b39de116102895780632f2ff15d1161026e5780632f2ff15d146103de5780633138809d146103fe57806336568abe1461041157600080fd5b8063278b39de1461039e5780632ee99d3e146103be57600080fd5b80631525ff7d116102ba5780631525ff7d1461032d5780631f6150231461034d578063248a9ca31461036057600080fd5b806301ffc9a7146102d65780630ae30cb01461030b575b600080fd5b3480156102e257600080fd5b506102f66102f1366004613826565b6109ee565b60405190151581526020015b60405180910390f35b34801561031757600080fd5b5061032b610326366004613880565b610a87565b005b34801561033957600080fd5b5061032b610348366004613880565b610b6b565b61032b61035b36600461389d565b610c25565b34801561036c57600080fd5b5061039061037b3660046138e3565b60009081526020819052604090206001015490565b604051908152602001610302565b3480156103aa57600080fd5b506103906103b93660046138fc565b611035565b3480156103ca57600080fd5b5061032b6103d936600461393f565b61107d565b3480156103ea57600080fd5b5061032b6103f93660046139b5565b611245565b61032b61040c3660046138fc565b611270565b34801561041d57600080fd5b5061032b61042c3660046139b5565b611304565b34801561043d57600080fd5b5061032b61044c3660046139e5565b6113aa565b34801561045d57600080fd5b5061032b61046c366004613880565b6114fa565b34801561047d57600080fd5b5061032b61048c366004613880565b61159a565b34801561049d57600080fd5b5061032b6104ac366004613a3b565b61164c565b61032b6104bf3660046138fc565b6117f6565b3480156104d057600080fd5b5061032b6104df366004613880565b611912565b3480156104f057600080fd5b5061032b6104ff366004613aad565b61199c565b34801561051057600080fd5b50600254610524906001600160a01b031681565b6040516001600160a01b039091168152602001610302565b34801561054857600080fd5b506105247f000000000000000000000000000000000000000000000000000000000000000081565b34801561057c57600080fd5b5061032b61058b3660046138fc565b611a97565b34801561059c57600080fd5b506105b06105ab366004613a3b565b611b63565b604051610302929190613b3a565b3480156105ca57600080fd5b506105d3611f04565b604051610302929190613b55565b3480156105ed57600080fd5b50600154610524906201000090046001600160a01b031681565b34801561061357600080fd5b5061032b610622366004613be7565b6120eb565b34801561063357600080fd5b506102f66106423660046139b5565b6000918252602082815260408084206001600160a01b0393909316845291905290205460ff1690565b34801561067757600080fd5b5061032b610686366004613880565b61216d565b34801561069757600080fd5b50610390600081565b3480156106ac57600080fd5b506001546106bb9061ffff1681565b60405161ffff9091168152602001610302565b3480156106da57600080fd5b506106e3612202565b6040516103029190613cb5565b3480156106fc57600080fd5b506103907f6a64baf327d646d1bca72653e2a075d15fd6ac6d8cbd7f6ee03fc55875e0fa8881565b34801561073057600080fd5b5061032b61073f366004613880565b6126d4565b34801561075057600080fd5b5061032b61075f366004613dfd565b612894565b34801561077057600080fd5b506102f661077f366004613880565b6001600160a01b031660009081526004602052604090205460ff1690565b3480156107a957600080fd5b50600354610524906001600160a01b031681565b3480156107c957600080fd5b506105246107d8366004613e2b565b6005602052600090815260409020546001600160a01b031681565b3480156107ff57600080fd5b5061032b61080e366004613880565b61298e565b34801561081f57600080fd5b5061032b61082e3660046139b5565b612ae3565b34801561083f57600080fd5b5061095361084e366004613880565b6004602081815260009283526040928390208054845161016081018652600183015461ffff8082168352620100008083048216848801526401000000008084048316858b01526601000000000000840483166060808701919091526801000000000000000090940483166080860152600287015460a0860152600387015460c08601529786015460e08501526005860154610100808601919091526006870154610120860152600787015461014086015260088701548a519485018b5260099097015480841685529182048316978401979097529690960486169681019690965260ff8216959382049094169363010000009091046001600160a01b03169290919086565b60405161030296959493929190613e46565b34801561097157600080fd5b506105d3612b09565b34801561098657600080fd5b5061032b6109953660046138fc565b612c71565b3480156109a657600080fd5b506103907f39780592ddb1e73f2a037f166ec57ba042082746f5c0b9fe56ea386e00369a9881565b3480156109da57600080fd5b506103906109e9366004613880565b612d41565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f7965db0b000000000000000000000000000000000000000000000000000000001480610a8157507f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316145b92915050565b6001600160a01b03811660009081526004602081905260409091209081015460058201546001909201918115610ae95760006003840155610ae97f0000000000000000000000000000000000000000000000000000000000000000858461311f565b8015610b0d5760006004840155600254610b0d906001600160a01b0316858361311f565b81151580610b1a57508015155b15610b655760408051838152602081018390526001600160a01b038616917f0146f1701c23c89f761280798d36d6c4e3acb349438456f5da8f83a2f5dd8cdc91015b60405180910390a25b50505050565b6000610b77813361314b565b6001600160a01b038216610bb7576040517f1a479e8200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600280547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0384169081179091556040519081527fc6a5dd316fe9d0339f2769deab7e31f64c8f5b101ffd85dfc9a83dbeaf2e69da906020015b60405180910390a15050565b7f6a64baf327d646d1bca72653e2a075d15fd6ac6d8cbd7f6ee03fc55875e0fa88610c50813361314b565b6001600160a01b0384166000908152600460205260408120600101805490919061271090610c889062010000900461ffff1686613edf565b610c929190613f4b565b905060008115610d43576003546040517f7700f17b0000000000000000000000000000000000000000000000000000000081526001600160a01b038a8116600483015289811660248301526044820185905290911690637700f17b906064016020604051808303816000875af1158015610d10573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d349190613f5f565b9050610d408183613f78565b91505b825460009061271090610d5a9061ffff1688613edf565b610d649190613f4b565b845490915060009061271090610d8690640100000000900461ffff1689613edf565b610d909190613f4b565b855490915060009061271090610db4906601000000000000900461ffff168a613edf565b610dbe9190613f4b565b865490915060009061271090610de49068010000000000000000900461ffff168b613edf565b610dee9190613f4b565b905060008415610e065780610e0281613f8f565b9150505b8315610e1a5780610e1681613f8f565b9150505b8215610e2e5780610e2a81613f8f565b9150505b8115610e425780610e3e81613f8f565b9150505b600060ff821615610e725760ff8216610e5b818a613fae565b610e65908a613f78565b610e6f9190613f4b565b90505b8515610e9e57610e828187613fc2565b955085896001016000828254610e989190613fc2565b90915550505b8415610eca57610eae8186613fc2565b945084896002016000828254610ec49190613fc2565b90915550505b8315610ef657610eda8185613fc2565b935083896003016000828254610ef09190613fc2565b90915550505b8215610f2257610f068184613fc2565b925082896004016000828254610f1c9190613fc2565b90915550505b8615610f74576001546201000090046001600160a01b0316610f5a57600354610f55906001600160a01b03168e8961311f565b610f74565b86896005016000828254610f6e9190613fc2565b90915550505b604080518781526020810189905290810186905260608101859052608081018490526001600160a01b038e16907fc73e6f5bcd4395126567ead7e4c14b7c52a5f0b9eabd3335a05e631646a127e39060a00160405180910390a2505050505050505050610fe285858561311f565b836001600160a01b03167f634235fcf5af0adbca1a405ec65f6f6c08f55e1f379c2c45cd10f23cb29e0e3161101686612d41565b6040805191825260208201879052015b60405180910390a25050505050565b6001600160a01b0382166000908152600460205260408120548290610100900461ffff1661106285612d41565b61106c9190613edf565b6110769190613f4b565b9392505050565b6000611089813361314b565b6000858486611098868b613fda565b6110a29190613fda565b6110ac9190613fda565b6110b69190613fda565b90508061ffff1661271014611102576040517f6104384400000000000000000000000000000000000000000000000000000000815261ffff821660048201526024015b60405180910390fd5b6000600460008a6001600160a01b03166001600160a01b031681526020019081526020016000206001019050878160000160006101000a81548161ffff021916908361ffff160217905550868160000160026101000a81548161ffff021916908361ffff160217905550858160000160046101000a81548161ffff021916908361ffff160217905550848160000160066101000a81548161ffff021916908361ffff160217905550838160000160086101000a81548161ffff021916908361ffff160217905550886001600160a01b03167f87512489f5b5226512d8f1bfede20d9e809ff4042f028da79c833ed04baa8397898989898960405161123295949392919061ffff95861681529385166020850152918416604084015283166060830152909116608082015260a00190565b60405180910390a2505050505050505050565b600082815260208190526040902060010154611261813361314b565b61126b83836131e3565b505050565b7f6a64baf327d646d1bca72653e2a075d15fd6ac6d8cbd7f6ee03fc55875e0fa8861129b813361314b565b826001600160a01b03167f812b76b477469edc716929cbf7ed54e3d9c1a68d8b9f8290dbabcda54d96fcbe6112cf85612d41565b6001600160a01b038616156112e457846112e6565b345b604080519283526020830191909152015b60405180910390a2505050565b6001600160a01b038116331461139c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201527f20726f6c657320666f722073656c66000000000000000000000000000000000060648201526084016110f9565b6113a6828261329f565b5050565b60006113b6813361314b565b6127108461ffff1611806113d857506127106113d28385613fda565b61ffff16115b1561140f576040517fa28c907300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604080516060808201835261ffff87811680845287821660208086018281528985168789018181526001600160a01b038f166000818152600486528b902099516009909a018054945192518916640100000000027fffffffffffffffffffffffffffffffffffffffffffffffffffff0000ffffffff938a1662010000027fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000009096169b9099169a909a1793909317169590951790965586519283528201529384015290917f4b481214fcdec41d9a7a06a45d5d931dada21892e48b04779c52d1c69d1e5f9f9101611026565b6001600160a01b03811660009081526004602052604090206003810154801580159061153657508154630100000090046001600160a01b031615155b1561126b5760006003830155815461155f90630100000090046001600160a01b0316848361311f565b826001600160a01b03167f1aa0fc6b9bc54a055d93b640b2c7313e0bd2944482e0b6b5fec7048bbdaacd81826040516112f791815260200190565b60006115a6813361314b565b6001600160a01b0382166115e6576040517f1a479e8200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600380547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0384169081179091556040519081527f678edf136de423322d705b75c3157ecfde496e1ad82aef725aa6deb9d94d4de290602001610c19565b6001546201000090046001600160a01b03163314611696576040517f4ca8886700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000806116a583850185614000565b6001600160a01b0381166000908152600460208181526040808420815161016081018352600182015461ffff808216835262010000820481169583019590955264010000000081048516938201939093526601000000000000830484166060820152680100000000000000009092049092166080820152600282015460a0820152600382015460c08201529181015460e0830152600581015461010083015260068101546101208301526007015461014082015292945090925083600281111561177157611771614022565b036117845761177f826126d4565b6117ef565b600183600281111561179857611798614022565b1480156117ad57508061014001518160c00151115b156117bb5761177f826114fa565b60028360028111156117cf576117cf614022565b1480156117e157506000816101200151115b156117ef576117ef82611912565b5050505050565b6001600160a01b03808316600090815260046020526040812054909184916301000000900416806118305761182b833361314b565b611872565b336001600160a01b03821614611872576040517f4ca8886700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600061187d86612d41565b90506001600160a01b03861661189f57611897868261333c565b3494506118c7565b6118b2866118ad8784613fc2565b61333c565b6118c76001600160a01b038716333088613398565b856001600160a01b03167fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c8660405161190291815260200190565b60405180910390a2505050505050565b6001600160a01b03811660009081526004602052604090206006810154600190910190801561126b57600354600060058401556001600160a01b031661195981858461311f565b604080516001600160a01b038381168252602082018590528616917fbd79daf3f76bb7aff9e8c05f8b107f48127baea8584624bdc2c632bb16cd221e9101610b5c565b6001600160a01b03808316600090815260046020526040812054909184916301000000900416806119d6576119d1833361314b565b611a18565b336001600160a01b03821614611a18576040517f4ca8886700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b03851660008181526004602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000ff1661010061ffff8a169081029190911790915591519182527f68288b0936dd994d19323ee940b2703e7bf46fae2552319d60dc1239ba52ec689101611026565b6001600160a01b0380831660009081526004602052604081205490918491630100000090041680611ad157611acc833361314b565b611b13565b336001600160a01b03821614611b13576040517f4ca8886700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b03851660008181526004602052604090819020600701869055517fc299d8f578e9777556514dc79b1c053fe9912dbc8424518c97262734a96e8095906110269087815260200190565b600060608180611b7585870187614000565b90925090506001826002811115611b8e57611b8e614022565b03611c81576001600160a01b038116600090815260046020818152604092839020835161016081018552600182015461ffff808216835262010000820481169483019490945264010000000081048416958201959095526601000000000000850483166060820152680100000000000000009094049091166080840152600281015460a0840152600381015460c084018190529181015460e0840152600581015461010084015260068101546101208401526007015461014083018190521015611c7b57600194508282604051602001611c69929190614051565b60405160208183030381529060405293505b50611efb565b60005b60015461ffff9081169082161015611ef95761ffff8082166000908152600560208181526040808420546001600160a01b039081168086526004808552838720845160c08082018752825460ff8116151583526101008082048d16848b01526301000000909104909616828801528651610160810188526001840154808d168252620100008082048e16838c01526401000000008083048f16848c0152660100000000000083048f16606085810191909152680100000000000000009093048f16608080860191909152600288015460a0808701919091526003890154968601969096529787015460e08501529b860154988301989098526006850154610120830152600785015461014083015280840191825260088501549584019590955287519485018852600990930154808c1685529586048b1697840197909752969093049097169287019290925291830194909452519091866002811115611dec57611dec614022565b148015611e04575060408201516001600160a01b0316155b15611e8f576000611e1484612d41565b905060006127108460a001516000015161ffff1683611e339190613edf565b611e3d9190613f4b565b8460800151611e4c9190613fc2565b905080821115611e8857600199508785604051602001611e6d929190614051565b60405160208183030381529060405298505050505050611ef9565b5050611ee3565b6002866002811115611ea357611ea3614022565b148015611eb557506000816101200151115b15611ee357600197508583604051602001611ed1929190614051565b60405160208183030381529060405296505b5050508080611ef1906140a4565b915050611c84565b505b50509250929050565b6060807f39780592ddb1e73f2a037f166ec57ba042082746f5c0b9fe56ea386e00369a98611f32813361314b565b60015460009061ffff1667ffffffffffffffff811115611f5457611f546140c5565b604051908082528060200260200182016040528015611f7d578160200160208202803683370190505b5060015490915060009061ffff1667ffffffffffffffff811115611fa357611fa36140c5565b604051908082528060200260200182016040528015611fcc578160200160208202803683370190505b50905060005b60015461ffff90811690821610156120e05761ffff81166000908152600560209081526040808320546001600160a01b03168084526004909252909120600281015480156120ca576000600283015561202c33848361311f565b826001600160a01b03167fca64dbcaf91abfb066e7a5163f1d135f8f48f2cbdb0395e3b35cc278ebbd340b8260405161206791815260200190565b60405180910390a282868561ffff1681518110612086576120866140f4565b60200260200101906001600160a01b031690816001600160a01b03168152505080858561ffff16815181106120bd576120bd6140f4565b6020026020010181815250505b50505080806120d8906140a4565b915050611fd2565b509093509150509091565b60006120f7813361314b565b6001600160a01b03831660008181526004602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001686151590811790915591519182527fe589eb036e62c07e307aa4d441bd39c81e8bd86f349eaacb0caa06b1477f7f9a91016112f7565b6000612179813361314b565b6001546001600160a01b038381166201000090920416146113a657600180547fffffffffffffffffffff0000000000000000000000000000000000000000ffff16620100006001600160a01b038516908102919091179091556040519081527f8bc03ae9a710727b3d59ae52c86167c6d2833076b076b23cbbcd5b381fef5d8890602001610c19565b60015460609060009061ffff1667ffffffffffffffff811115612227576122276140c5565b60405190808252806020026020018201604052801561231257816020015b6040805160a08082018352600080835260208084018290526060848601819052808501819052855160c08082018852848252818401859052818801859052875161016081018952858152808501869052808901869052808401869052608080820187905281880187905291810186905260e08101869052610100810186905261012081018690526101408101869052828401528082018590528751928301885284835292820184905295810192909252918401528101919091528152602001906001900390816122455790505b50905060005b60015461ffff90811690821610156126ce5761ffff8181166000908152600560208181526040808420546001600160a01b03908116808652600480855295839020835160c08082018652825460ff8116151583526101008082048c16848a01526301000000909104909516828701528551610160810187526001840154808c168252620100008082048d16838b01526401000000008083048e16848b0152660100000000000083048e16606080860191909152680100000000000000009093048e16608080860191909152600288015460a0808701919091526003890154968601969096529c87015460e08501529a86015497830197909752600685015461012083015260078501546101408301528084019190915260088401549983019990995285519889018652600990920154808a16895293840489169588019590955294909104909516908401529081019190915281612533576040518060a00160405280601260ff168152602001836001600160a01b031681526020016040518060400160405280600381526020017f455448000000000000000000000000000000000000000000000000000000000081525081526020016040518060400160405280600381526020017f4554480000000000000000000000000000000000000000000000000000000000815250815260200182815250848461ffff1681518110612523576125236140f4565b60200260200101819052506126b9565b60008290506040518060a00160405280826001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612581573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125a59190614123565b60ff168152602001846001600160a01b03168152602001826001600160a01b03166306fdde036040518163ffffffff1660e01b8152600401600060405180830381865afa1580156125fa573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526126229190810190614146565b8152602001826001600160a01b03166395d89b416040518163ffffffff1660e01b8152600401600060405180830381865afa158015612665573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261268d9190810190614146565b815260200183815250858561ffff16815181106126ac576126ac6140f4565b6020026020010181905250505b505080806126c6906140a4565b915050612318565b50919050565b6001600160a01b0381166000908152600460205260408120906126f683612d41565b6009830154909150600090612710906127139061ffff1684613edf565b61271d9190613f4b565b836008015461272c9190613fc2565b8354909150630100000090046001600160a01b031615801561274d57508082115b15610b655760008360080154836127649190613f78565b6009850154909150600090612710906127879062010000900461ffff1684613edf565b6127919190613f4b565b6009860154909150600090612710906127b690640100000000900461ffff1685613edf565b6127c09190613f4b565b90506127db87826127d18589613f78565b6118ad9190613f78565b600486018054600588018054600093849055929055906128257f00000000000000000000000000000000000000000000000000000000000000008a6128208786613fc2565b61311f565b600254612840906001600160a01b03168a6128208685613fc2565b6001600160a01b0389167f6842359a4c3b5f98197bd29d5a317cd8c919e10ef467c746b9b256e99157b08e6128758685613fc2565b61287f8685613fc2565b60408051928352602083019190915201611232565b6001600160a01b03808316600090815260046020526040812054909184916301000000900416806128ce576128c9833361314b565b612910565b336001600160a01b03821614612910576040517f4ca8886700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b0385811660008181526004602090815260409182902080547fffffffffffffffffff0000000000000000000000000000000000000000ffffff166301000000958a16958602179055905192835290917fc991529379552af6d0789cc899d5180a270a4bc3b1a3892adcac3d95f93dda1d9101611026565b600061299a813361314b565b60015461ffff1615612a325760005b60015461ffff9081169082161015612a305761ffff81166000908152600560205260409020546001600160a01b03808516911603612a1e576040517ff49f09990000000000000000000000000000000000000000000000000000000081526001600160a01b03841660048201526024016110f9565b80612a28816140a4565b9150506129a9565b505b6001805461ffff908116600090815260056020526040812080547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b03871617905582548392612a8b91849116613fda565b92506101000a81548161ffff021916908361ffff1602179055507fe473c74f34be27c1464d6624f14a0d7fd4e301cbfa29c3eba425d378c8a7ebe082604051610c1991906001600160a01b0391909116815260200190565b600082815260208190526040902060010154612aff813361314b565b61126b838361329f565b600154606090819060009061ffff1667ffffffffffffffff811115612b3057612b306140c5565b604051908082528060200260200182016040528015612b59578160200160208202803683370190505b5060015490915060009061ffff1667ffffffffffffffff811115612b7f57612b7f6140c5565b604051908082528060200260200182016040528015612ba8578160200160208202803683370190505b50905060005b60015461ffff9081169082161015612c675761ffff81166000908152600560209081526040808320546001600160a01b0316808452600490925290912060028101548015612c515782868561ffff1681518110612c0d57612c0d6140f4565b60200260200101906001600160a01b031690816001600160a01b03168152505080858561ffff1681518110612c4457612c446140f4565b6020026020010181815250505b5050508080612c5f906140a4565b915050612bae565b5090939092509050565b6001600160a01b0380831660009081526004602052604081205490918491630100000090041680612cab57612ca6833361314b565b612ced565b336001600160a01b03821614612ced576040517f4ca8886700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612cfb85856127d188612d41565b612d0633868661311f565b846001600160a01b03167f884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a94243648560405161102691815260200190565b6000806001600160a01b038316612d59575047612ddd565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b038416906370a0823190602401602060405180830381865afa158015612db6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612dda9190613f5f565b90505b6001600160a01b038316600090815260046020818152604092839020835161016081018552600182015461ffff808216835262010000820481169483019490945264010000000081048416958201959095526601000000000000850483166060820152680100000000000000009094049091166080840152600281015460a08401819052600382015460c085018190529282015460e08501819052600583015461010086018190526006840154610120870181905260079094015461014087015292939091612eac9087613f78565b612eb69190613f78565b612ec09190613f78565b612eca9190613f78565b612ed49190613f78565b949350505050565b60606000612eeb836002613edf565b612ef6906002613fc2565b67ffffffffffffffff811115612f0e57612f0e6140c5565b6040519080825280601f01601f191660200182016040528015612f38576020820181803683370190505b5090507f300000000000000000000000000000000000000000000000000000000000000081600081518110612f6f57612f6f6140f4565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053507f780000000000000000000000000000000000000000000000000000000000000081600181518110612fd257612fd26140f4565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350600061300e846002613edf565b613019906001613fc2565b90505b60018111156130b6577f303132333435363738396162636465660000000000000000000000000000000085600f166010811061305a5761305a6140f4565b1a60f81b828281518110613070576130706140f4565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535060049490941c936130af816141e8565b905061301c565b508315611076576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e7460448201526064016110f9565b6001600160a01b0382166131375761126b8382613449565b61126b6001600160a01b0383168483613596565b6000828152602081815260408083206001600160a01b038516845290915290205460ff166113a657613187816001600160a01b03166014612edc565b613192836020612edc565b6040516020016131a392919061421d565b60408051601f19818403018152908290527f08c379a00000000000000000000000000000000000000000000000000000000082526110f99160040161429e565b6000828152602081815260408083206001600160a01b038516845290915290205460ff166113a6576000828152602081815260408083206001600160a01b0385168452909152902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905561325b3390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b6000828152602081815260408083206001600160a01b038516845290915290205460ff16156113a6576000828152602081815260408083206001600160a01b038516808552925280832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b6001600160a01b03821660008181526004602052604090819020600801839055517f492a63a7d96d749f455133f12ab1ea15a7dd0a5d5f6ae04694244a2761df3f8c9061338c9084815260200190565b60405180910390a25050565b6040516001600160a01b0380851660248301528316604482015260648101829052610b659085907f23b872dd00000000000000000000000000000000000000000000000000000000906084015b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909316929092179091526135df565b804710156134b3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e636500000060448201526064016110f9565b6000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114613500576040519150601f19603f3d011682016040523d82523d6000602084013e613505565b606091505b505090508061126b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603a60248201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260448201527f6563697069656e74206d6179206861766520726576657274656400000000000060648201526084016110f9565b6040516001600160a01b03831660248201526044810182905261126b9084907fa9059cbb00000000000000000000000000000000000000000000000000000000906064016133e5565b6000613634826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166136de9092919063ffffffff16565b80519091501561126b578080602001905181019061365291906142b1565b61126b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f7420737563636565640000000000000000000000000000000000000000000060648201526084016110f9565b6060612ed48484600085856001600160a01b0385163b61375a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016110f9565b600080866001600160a01b0316858760405161377691906142ce565b60006040518083038185875af1925050503d80600081146137b3576040519150601f19603f3d011682016040523d82523d6000602084013e6137b8565b606091505b50915091506137c88282866137d3565b979650505050505050565b606083156137e2575081611076565b8251156137f25782518084602001fd5b816040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016110f9919061429e565b60006020828403121561383857600080fd5b81357fffffffff000000000000000000000000000000000000000000000000000000008116811461107657600080fd5b6001600160a01b038116811461387d57600080fd5b50565b60006020828403121561389257600080fd5b813561107681613868565b600080600080608085870312156138b357600080fd5b84356138be81613868565b935060208501356138ce81613868565b93969395505050506040820135916060013590565b6000602082840312156138f557600080fd5b5035919050565b6000806040838503121561390f57600080fd5b823561391a81613868565b946020939093013593505050565b803561ffff8116811461393a57600080fd5b919050565b60008060008060008060c0878903121561395857600080fd5b863561396381613868565b955061397160208801613928565b945061397f60408801613928565b935061398d60608801613928565b925061399b60808801613928565b91506139a960a08801613928565b90509295509295509295565b600080604083850312156139c857600080fd5b8235915060208301356139da81613868565b809150509250929050565b600080600080608085870312156139fb57600080fd5b8435613a0681613868565b9350613a1460208601613928565b9250613a2260408601613928565b9150613a3060608601613928565b905092959194509250565b60008060208385031215613a4e57600080fd5b823567ffffffffffffffff80821115613a6657600080fd5b818501915085601f830112613a7a57600080fd5b813581811115613a8957600080fd5b866020828501011115613a9b57600080fd5b60209290920196919550909350505050565b60008060408385031215613ac057600080fd5b8235613acb81613868565b9150613ad960208401613928565b90509250929050565b60005b83811015613afd578181015183820152602001613ae5565b83811115610b655750506000910152565b60008151808452613b26816020860160208601613ae2565b601f01601f19169290920160200192915050565b8215158152604060208201526000612ed46040830184613b0e565b604080825283519082018190526000906020906060840190828701845b82811015613b975781516001600160a01b031684529284019290840190600101613b72565b5050508381038285015284518082528583019183019060005b81811015613bcc57835183529284019291840191600101613bb0565b5090979650505050505050565b801515811461387d57600080fd5b60008060408385031215613bfa57600080fd5b8235613c0581613868565b915060208301356139da81613bd9565b805161ffff1682526020810151613c32602084018261ffff169052565b506040810151613c48604084018261ffff169052565b506060810151613c5e606084018261ffff169052565b506080810151613c74608084018261ffff169052565b5060a0818101519083015260c0808201519083015260e080820151908301526101008082015190830152610120808201519083015261014090810151910152565b60006020808301818452808551808352604092508286019150828160051b87010184880160005b83811015613def577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc089840301855281516102c060ff8251168552888201516001600160a01b038082168b880152898401519150828a880152613d4183880183613b0e565b9250606091508184015187840383890152613d5c8482613b0e565b935050608080850151945084511515818901528b85015160a061ffff8216818b0152838d8801511660c08b0152848701519450613d9c60e08b0186613c15565b918601516102408a0152509390930151805161ffff908116610260890152602082015181166102808901526040820151166102a08801529250613ddc9050565b9588019593505090860190600101613cdc565b509098975050505050505050565b60008060408385031215613e1057600080fd5b8235613e1b81613868565b915060208301356139da81613868565b600060208284031215613e3d57600080fd5b61107682613928565b861515815261ffff861660208201526001600160a01b03851660408201526102408101613e766060830186613c15565b6101c08201849052825161ffff9081166101e084015260208401518116610200840152604084015116610220830152979650505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615613f1757613f17613eb0565b500290565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600082613f5a57613f5a613f1c565b500490565b600060208284031215613f7157600080fd5b5051919050565b600082821015613f8a57613f8a613eb0565b500390565b600060ff821660ff8103613fa557613fa5613eb0565b60010192915050565b600082613fbd57613fbd613f1c565b500690565b60008219821115613fd557613fd5613eb0565b500190565b600061ffff808316818516808303821115613ff757613ff7613eb0565b01949350505050565b6000806040838503121561401357600080fd5b823560038110613e1b57600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b604081016003841061408c577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b9281526001600160a01b039190911660209091015290565b600061ffff8083168181036140bb576140bb613eb0565b6001019392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60006020828403121561413557600080fd5b815160ff8116811461107657600080fd5b60006020828403121561415857600080fd5b815167ffffffffffffffff8082111561417057600080fd5b818401915084601f83011261418457600080fd5b815181811115614196576141966140c5565b604051601f8201601f19908116603f011681019083821181831017156141be576141be6140c5565b816040528281528760208487010111156141d757600080fd5b6137c8836020830160208801613ae2565b6000816141f7576141f7613eb0565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b7f416363657373436f6e74726f6c3a206163636f756e7420000000000000000000815260008351614255816017850160208801613ae2565b7f206973206d697373696e6720726f6c65200000000000000000000000000000006017918401918201528351614292816028840160208801613ae2565b01602801949350505050565b6020815260006110766020830184613b0e565b6000602082840312156142c357600080fd5b815161107681613bd9565b600082516142e0818460208701613ae2565b919091019291505056fea26469706673582212202ffe9dcdcaa4ecc8b053496841d7fe6006bd33f4361a848cb8886f8aae56ccf064736f6c634300080e00330000000000000000000000002c305888a456e7004663bc12a74395e637eabcbc0000000000000000000000008343f2dc53e55dbbe626b9d8b4fbbb4e37dc218b000000000000000000000000a368c0ab32dd9bc0500075c0c8397b6b0168e432

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

0000000000000000000000002c305888a456e7004663bc12a74395e637eabcbc0000000000000000000000008343f2dc53e55dbbe626b9d8b4fbbb4e37dc218b000000000000000000000000a368c0ab32dd9bc0500075c0c8397b6b0168e432

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

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


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