Contract 0xb4b9fca2553fce552aba3eb33dee2b9057bf88d1

 

Contract Overview

Balance:
0 BNB

BNB Value:
$0.00

Token:
 
Txn Hash Method
Block
From
To
Value [Txn Fee]
0x68117d89ac6b096eb4d29346bf3f5b2d7e1da03b6d42b8ae2c379c2a66ea2b87Request Burn126949152021-11-16 11:02:5418 days 2 hrs ago0x15963804e1fdae4cf707de3476e8d672520a391d IN  0xb4b9fca2553fce552aba3eb33dee2b9057bf88d10 BNB0.00081297
0xd0afb6c75bdabf080942fa56bdece4613e7e5b3d1fe2ed6a770d25ddde54c87bRequest Burn126948012021-11-16 10:57:0618 days 3 hrs ago0x15963804e1fdae4cf707de3476e8d672520a391d IN  0xb4b9fca2553fce552aba3eb33dee2b9057bf88d10 BNB0.00081297
0xcc518a8da1df7176e73a950488cac4388a1a493986babb5c0029e445125d5c4cRequest Burn126939852021-11-16 10:10:0718 days 3 hrs ago0x15963804e1fdae4cf707de3476e8d672520a391d IN  0xb4b9fca2553fce552aba3eb33dee2b9057bf88d10 BNB0.00081297
0xe33a8f90c01f8e024c797d561232a44cb5aa3e8cbe7c81fef0d5345442d92ed9Request Burn126920772021-11-16 8:27:4318 days 5 hrs ago0x3b8b929281444176c2cf52c024f2b06d7e688714 IN  0xb4b9fca2553fce552aba3eb33dee2b9057bf88d10 BNB0.00073797
0xf48bb8be558b8c17cf6dab5c1c96b84abc05bfa524aad07d4a6512d47d06fa74Request Burn126917782021-11-16 8:11:3318 days 5 hrs ago0x3b8b929281444176c2cf52c024f2b06d7e688714 IN  0xb4b9fca2553fce552aba3eb33dee2b9057bf88d10 BNB0.00081297
0xbf44101052a77e6664191f559745869ef363c717a1681bdde1af53d498b00383Force Request Mi...125882612021-11-12 14:37:5921 days 23 hrs ago0xcc7af7cd4d46804163ae90a7345f9536364f16d2 IN  0xb4b9fca2553fce552aba3eb33dee2b9057bf88d10.003 BNB0.000591985
0x1c13fc225b84b8dc225ab119a373559a132d6fb9748bfcf02d5056022adc632eForce Request Mi...125877422021-11-12 14:09:4021 days 23 hrs ago0xcc7af7cd4d46804163ae90a7345f9536364f16d2 IN  0xb4b9fca2553fce552aba3eb33dee2b9057bf88d10.003 BNB0.000591985
0x44c2a007e0e3e7bd0e06dae7cc04163cb21b7f32fcfbce488f32c6f9a230d9a8Request Mint125868602021-11-12 13:20:1022 days 38 mins ago0xcc7af7cd4d46804163ae90a7345f9536364f16d2 IN  0xb4b9fca2553fce552aba3eb33dee2b9057bf88d10.003 BNB0.00079677
0x30be1bf304ada3ad824c91df2f431552e4f7e86c229940291c8b74684c07608cRequest Burn123484572021-11-04 2:24:1330 days 11 hrs ago0x57aec0f61dc71a15a976f20cd485a40cbb5a7fff IN  0xb4b9fca2553fce552aba3eb33dee2b9057bf88d10 BNB0.00073797
0x80b2206227835b2667ba791f625054c82d93fa60d08fb6db0b38f0f815c8d1e0Request Burn123476422021-11-04 1:42:4630 days 12 hrs ago0x6c1a73b3db0999484255ad71bb203d0e073100b6 IN  0xb4b9fca2553fce552aba3eb33dee2b9057bf88d10 BNB0.00073797
0x144ce493c9840627dc2c929133b2d13e240f795b65fa80754e01056f9ffe5c7dRequest Burn123301532021-11-03 10:28:5731 days 3 hrs ago0x6c1a73b3db0999484255ad71bb203d0e073100b6 IN  0xb4b9fca2553fce552aba3eb33dee2b9057bf88d10 BNB0.00081297
0x06b5c4da9b37a3de2fb8d6cdbf09f358853b192e7d846a846f05e7c50847ef4bRequest Burn123300702021-11-03 10:24:4831 days 3 hrs ago0x57aec0f61dc71a15a976f20cd485a40cbb5a7fff IN  0xb4b9fca2553fce552aba3eb33dee2b9057bf88d10 BNB0.00081297
0x3733caefe04dcf872961c1e9f7396d06b54c3e1dca2a42acea12573d38f121290x4327c083121622162021-10-28 11:10:1737 days 2 hrs ago0x93751db31679c3e61348d6dfbcb0a9641fbb0269 IN  0xb4b9fca2553fce552aba3eb33dee2b9057bf88d10 BNB0.00106239
0x9b8e206d632a09c27dcc40ddfb08249630f4be71cc78551481d85b94a0213915Request Mint121606912021-10-28 9:53:1137 days 4 hrs ago0xfe4493ce82fee8dcf1a4ea59026509237fc4cf75 IN  0xb4b9fca2553fce552aba3eb33dee2b9057bf88d10.003 BNB0.00079677
0x28d902bb28e63b5d1cebd6725c27a40dc2b107f1664985a8afc6fee6ccd312ee0x4327c083121317562021-10-27 9:23:2238 days 4 hrs ago0x93751db31679c3e61348d6dfbcb0a9641fbb0269 IN  0xb4b9fca2553fce552aba3eb33dee2b9057bf88d10 BNB0.00091251
0xc10d250d8c173fc438d9e60bdcae172efcfcf2b5b05db6564320d9f2c0fc3b0fRequest Mint121307102021-10-27 8:28:5638 days 5 hrs ago0x42e8e7446b01a8b907adccd0094012199592870b IN  0xb4b9fca2553fce552aba3eb33dee2b9057bf88d10.003 BNB0.00079677
0xbbaefe5159f64ad55f688062b06a4711bd190b35cf2b83e9050fa0a2a6a849980x4327c083121285222021-10-27 6:37:5338 days 7 hrs ago0x93751db31679c3e61348d6dfbcb0a9641fbb0269 IN  0xb4b9fca2553fce552aba3eb33dee2b9057bf88d10 BNB0.00098721
0xfb933e6bdc2fa8c22fd129bbfa1b643567537af70eb3cf98adcc57b18ac01060Request Mint121262752021-10-27 4:45:0138 days 9 hrs ago0x42e8e7446b01a8b907adccd0094012199592870b IN  0xb4b9fca2553fce552aba3eb33dee2b9057bf88d10.003 BNB0.00079677
0x312cff7b85a944d48e048eafd1bef93cd7508630f92073696548ebc80b69684fRequest Mint120980472021-10-26 5:01:4839 days 8 hrs ago0xf300152fba1d672cf15c0f2e78a9588f31230e1d IN  0xb4b9fca2553fce552aba3eb33dee2b9057bf88d10.003 BNB0.00079677
0x9b55138f5cf5724f0b7322c4828ef534bb764f489c91374190d0aa0814cde62fRequest Burn120486312021-10-24 11:23:2841 days 2 hrs ago0x15963804e1fdae4cf707de3476e8d672520a391d IN  0xb4b9fca2553fce552aba3eb33dee2b9057bf88d10 BNB0.00081297
0x8df3a1cb4876d3322b70917d04a9be2965c8b14bc472a1865df58939b7e8c75dRequest Burn120214292021-10-23 12:35:0542 days 1 hr ago0x00dc9bfd8ca42b83e111dd1e02533347fd586bf0 IN  0xb4b9fca2553fce552aba3eb33dee2b9057bf88d10 BNB0.00081249
0x60d98ce6f76f1199b80f5f4e26d2f5011c7d8184683f90af6b37beb4a0ed5809Request Mint119950952021-10-22 14:26:0842 days 23 hrs ago0xf86fd7d9402371cf268b1ea47ea67ee9b5f4e1b8 IN  0xb4b9fca2553fce552aba3eb33dee2b9057bf88d10.003 BNB0.00079677
0x328acbd4bc7ea228490d5196c8e76f94de769505387b87665a2c84038a8fa4d4Request Burn119895282021-10-22 9:45:4343 days 4 hrs ago0xe6a752fc11d0ededf68ab56405fd9cde6cb968d1 IN  0xb4b9fca2553fce552aba3eb33dee2b9057bf88d10 BNB0.00088791
0x3b93f582efd45bb594377dc69c441e84eba41c8037c487a09d679fefc795e6c8Add Group119607292021-10-21 9:01:3144 days 4 hrs agoDeCus: Deployer IN  0xb4b9fca2553fce552aba3eb33dee2b9057bf88d10 BNB0.00188961
0xe5729b6231d5634f13c4431cc073c3077810c611864cf6cfd88d3e6547e36cc2Add Group119607252021-10-21 9:01:1744 days 4 hrs agoDeCus: Deployer IN  0xb4b9fca2553fce552aba3eb33dee2b9057bf88d10 BNB0.00188973
[ Download CSV Export 
Latest 25 internal transaction
Parent Txn Hash Block From To Value
0xbf44101052a77e6664191f559745869ef363c717a1681bdde1af53d498b00383125882612021-11-12 14:37:5921 days 23 hrs ago 0xb4b9fca2553fce552aba3eb33dee2b9057bf88d1 0x61a6784726b8d34042d50eac42ebfe128ee898450.003 BNB
0x1c13fc225b84b8dc225ab119a373559a132d6fb9748bfcf02d5056022adc632e125877422021-11-12 14:09:4021 days 23 hrs ago 0xb4b9fca2553fce552aba3eb33dee2b9057bf88d1 0x61a6784726b8d34042d50eac42ebfe128ee898450.003 BNB
0x44c2a007e0e3e7bd0e06dae7cc04163cb21b7f32fcfbce488f32c6f9a230d9a8125868602021-11-12 13:20:1022 days 38 mins ago 0xb4b9fca2553fce552aba3eb33dee2b9057bf88d1 0x61a6784726b8d34042d50eac42ebfe128ee898450.003 BNB
0x9b8e206d632a09c27dcc40ddfb08249630f4be71cc78551481d85b94a0213915121606912021-10-28 9:53:1137 days 4 hrs ago 0xb4b9fca2553fce552aba3eb33dee2b9057bf88d1 0x61a6784726b8d34042d50eac42ebfe128ee898450.003 BNB
0xc10d250d8c173fc438d9e60bdcae172efcfcf2b5b05db6564320d9f2c0fc3b0f121307102021-10-27 8:28:5638 days 5 hrs ago 0xb4b9fca2553fce552aba3eb33dee2b9057bf88d1 0x61a6784726b8d34042d50eac42ebfe128ee898450.003 BNB
0xfb933e6bdc2fa8c22fd129bbfa1b643567537af70eb3cf98adcc57b18ac01060121262752021-10-27 4:45:0138 days 9 hrs ago 0xb4b9fca2553fce552aba3eb33dee2b9057bf88d1 0x61a6784726b8d34042d50eac42ebfe128ee898450.003 BNB
0x312cff7b85a944d48e048eafd1bef93cd7508630f92073696548ebc80b69684f120980472021-10-26 5:01:4839 days 8 hrs ago 0xb4b9fca2553fce552aba3eb33dee2b9057bf88d1 0x61a6784726b8d34042d50eac42ebfe128ee898450.003 BNB
0x60d98ce6f76f1199b80f5f4e26d2f5011c7d8184683f90af6b37beb4a0ed5809119950952021-10-22 14:26:0842 days 23 hrs ago 0xb4b9fca2553fce552aba3eb33dee2b9057bf88d1 0x61a6784726b8d34042d50eac42ebfe128ee898450.003 BNB
0x18dd0a35ab824121db429da7d0925986bb8cb6725b403889d16de4ea5a161229119579292021-10-21 6:38:5144 days 7 hrs ago 0xb4b9fca2553fce552aba3eb33dee2b9057bf88d1 0x61a6784726b8d34042d50eac42ebfe128ee898450.003 BNB
0x5e3156c6c465de82ef0587d3a06edeeb7b5d569580c80fd717c95399443de489119528112021-10-21 2:16:1144 days 11 hrs ago 0xb4b9fca2553fce552aba3eb33dee2b9057bf88d1 0x61a6784726b8d34042d50eac42ebfe128ee898450.003 BNB
0x2847499f48c827071ecd6d7b07e64439d84b96d5d452d7f7d74d7141f0d85bfe119480842021-10-20 22:18:1744 days 15 hrs ago 0xb4b9fca2553fce552aba3eb33dee2b9057bf88d1 0x61a6784726b8d34042d50eac42ebfe128ee898450.003 BNB
0x60153804f060fa57af557cd229274c91fd703677c702f76de43d2b549baf718f119457352021-10-20 20:20:2744 days 17 hrs ago 0xb4b9fca2553fce552aba3eb33dee2b9057bf88d1 0x61a6784726b8d34042d50eac42ebfe128ee898450.003 BNB
0xbe1208653c289e15791f6857e2da59522ddfd68ed02ae4a97ec59ce6685629f5119455032021-10-20 20:08:5144 days 17 hrs ago 0xb4b9fca2553fce552aba3eb33dee2b9057bf88d1 0x61a6784726b8d34042d50eac42ebfe128ee898450.003 BNB
0xccf99a0a5630ba11e3170c544f33bbb7e488f1950031fafc0cbd5f5c24643af1119454802021-10-20 20:07:4244 days 17 hrs ago 0xb4b9fca2553fce552aba3eb33dee2b9057bf88d1 0x61a6784726b8d34042d50eac42ebfe128ee898450.003 BNB
0xba0ad1bd4558bcc5f6e46bfb7688a53f0a08b3917bcf5c9e268a10d55f25f6a7119432492021-10-20 18:13:2944 days 19 hrs ago 0xb4b9fca2553fce552aba3eb33dee2b9057bf88d1 0x61a6784726b8d34042d50eac42ebfe128ee898450.003 BNB
0x2d973d00150f2e1a63f17d26a8d997b8403acfb817d8ead81f16152f86e64d29119432272021-10-20 18:12:2344 days 19 hrs ago 0xb4b9fca2553fce552aba3eb33dee2b9057bf88d1 0x61a6784726b8d34042d50eac42ebfe128ee898450.003 BNB
0x487eb4537d276b4b918cabf61a03f5cb2087d5747d4c72cc16cdd402647d10f9119432022021-10-20 18:11:0844 days 19 hrs ago 0xb4b9fca2553fce552aba3eb33dee2b9057bf88d1 0x61a6784726b8d34042d50eac42ebfe128ee898450.003 BNB
0xce6dd556561d2d8b54d28bde5245443dab7bce3ddc332e1eff04ddbfd9120be5119431562021-10-20 18:08:5044 days 19 hrs ago 0xb4b9fca2553fce552aba3eb33dee2b9057bf88d1 0x61a6784726b8d34042d50eac42ebfe128ee898450.003 BNB
0xc029154c509dd4a3eae17bccd9cec17faf56610383e692706c3bac25e2ab4bed119426432021-10-20 17:43:0944 days 20 hrs ago 0xb4b9fca2553fce552aba3eb33dee2b9057bf88d1 0x61a6784726b8d34042d50eac42ebfe128ee898450.003 BNB
0x9b9f374c91513e1c58edda1e94ed1e840cd32d1329a182caca36325aab92f681119426012021-10-20 17:41:0344 days 20 hrs ago 0xb4b9fca2553fce552aba3eb33dee2b9057bf88d1 0x61a6784726b8d34042d50eac42ebfe128ee898450.003 BNB
0xcc8ad4686d719093023d783fcbcd2f85b3e264565d0525a863f98e36b136431c119425912021-10-20 17:40:3344 days 20 hrs ago 0xb4b9fca2553fce552aba3eb33dee2b9057bf88d1 0x61a6784726b8d34042d50eac42ebfe128ee898450.003 BNB
0x65f22a99ebc53364c91f2c871d547bee120c6d265b695b1726adbecd9e55cf2d119425612021-10-20 17:39:0344 days 20 hrs ago 0xb4b9fca2553fce552aba3eb33dee2b9057bf88d1 0x61a6784726b8d34042d50eac42ebfe128ee898450.003 BNB
0x974d4c4ec40919250745b426060259135c00e4cd461c13c9312a3b299f7c53b5119424992021-10-20 17:35:5744 days 20 hrs ago 0xb4b9fca2553fce552aba3eb33dee2b9057bf88d1 0x61a6784726b8d34042d50eac42ebfe128ee898450.003 BNB
0x9658bb3568c489eaadc953dd72922502e8e5709e69b9e12e3e2454fa2849ebae119424672021-10-20 17:34:2144 days 20 hrs ago 0xb4b9fca2553fce552aba3eb33dee2b9057bf88d1 0x61a6784726b8d34042d50eac42ebfe128ee898450.003 BNB
0xaec120f72c0633a116a22a05d9768fa29629270796eeb2f92c4d68154e778828119424162021-10-20 17:31:4844 days 20 hrs ago 0xb4b9fca2553fce552aba3eb33dee2b9057bf88d1 0x61a6784726b8d34042d50eac42ebfe128ee898450.003 BNB
[ Download CSV Export 
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
DeCusSystem

Compiler Version
v0.7.6+commit.7338295f

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion, MIT license

Contract Source Code (Solidity Standard Json-Input format)

File 1 of 18 : AccessControl.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.7.0;

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

/**
 * @dev Contract module that allows children to implement role-based access
 * control mechanisms.
 *
 * 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 {
    using EnumerableSet for EnumerableSet.AddressSet;
    using Address for address;

    struct RoleData {
        EnumerableSet.AddressSet members;
        bytes32 adminRole;
    }

    mapping (bytes32 => RoleData) private _roles;

    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;

    /**
     * @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 {_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) public view returns (bool) {
        return _roles[role].members.contains(account);
    }

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

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

    /**
     * @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 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 {
        require(hasRole(_roles[role].adminRole, _msgSender()), "AccessControl: sender must be an admin to grant");

        _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 {
        require(hasRole(_roles[role].adminRole, _msgSender()), "AccessControl: sender must be an admin to revoke");

        _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 granted `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `account`.
     */
    function renounceRole(bytes32 role, address account) public virtual {
        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}.
     * ====
     */
    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 {
        emit RoleAdminChanged(role, _roles[role].adminRole, adminRole);
        _roles[role].adminRole = adminRole;
    }

    function _grantRole(bytes32 role, address account) private {
        if (_roles[role].members.add(account)) {
            emit RoleGranted(role, account, _msgSender());
        }
    }

    function _revokeRole(bytes32 role, address account) private {
        if (_roles[role].members.remove(account)) {
            emit RoleRevoked(role, account, _msgSender());
        }
    }
}

File 2 of 18 : EIP712.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

/**
 * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
 *
 * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible,
 * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding
 * they need in their contracts using a combination of `abi.encode` and `keccak256`.
 *
 * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
 * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
 * ({_hashTypedDataV4}).
 *
 * The implementation of the domain separator was designed to be as efficient as possible while still properly updating
 * the chain id to protect against replay attacks on an eventual fork of the chain.
 *
 * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
 * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
 *
 * _Available since v3.4._
 */
abstract contract EIP712 {
    /* solhint-disable var-name-mixedcase */
    // Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to
    // invalidate the cached domain separator if the chain id changes.
    bytes32 private immutable _CACHED_DOMAIN_SEPARATOR;
    uint256 private immutable _CACHED_CHAIN_ID;

    bytes32 private immutable _HASHED_NAME;
    bytes32 private immutable _HASHED_VERSION;
    bytes32 private immutable _TYPE_HASH;
    /* solhint-enable var-name-mixedcase */

    /**
     * @dev Initializes the domain separator and parameter caches.
     *
     * The meaning of `name` and `version` is specified in
     * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
     *
     * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
     * - `version`: the current major version of the signing domain.
     *
     * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
     * contract upgrade].
     */
    constructor(string memory name, string memory version) {
        bytes32 hashedName = keccak256(bytes(name));
        bytes32 hashedVersion = keccak256(bytes(version));
        bytes32 typeHash = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
        _HASHED_NAME = hashedName;
        _HASHED_VERSION = hashedVersion;
        _CACHED_CHAIN_ID = _getChainId();
        _CACHED_DOMAIN_SEPARATOR = _buildDomainSeparator(typeHash, hashedName, hashedVersion);
        _TYPE_HASH = typeHash;
    }

    /**
     * @dev Returns the domain separator for the current chain.
     */
    function _domainSeparatorV4() internal view virtual returns (bytes32) {
        if (_getChainId() == _CACHED_CHAIN_ID) {
            return _CACHED_DOMAIN_SEPARATOR;
        } else {
            return _buildDomainSeparator(_TYPE_HASH, _HASHED_NAME, _HASHED_VERSION);
        }
    }

    function _buildDomainSeparator(bytes32 typeHash, bytes32 name, bytes32 version) private view returns (bytes32) {
        return keccak256(
            abi.encode(
                typeHash,
                name,
                version,
                _getChainId(),
                address(this)
            )
        );
    }

    /**
     * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
     * function returns the hash of the fully encoded EIP712 message for this domain.
     *
     * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
     *
     * ```solidity
     * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
     *     keccak256("Mail(address to,string contents)"),
     *     mailTo,
     *     keccak256(bytes(mailContents))
     * )));
     * address signer = ECDSA.recover(digest, signature);
     * ```
     */
    function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
        return keccak256(abi.encodePacked("\x19\x01", _domainSeparatorV4(), structHash));
    }

    function _getChainId() private view returns (uint256 chainId) {
        this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
        // solhint-disable-next-line no-inline-assembly
        assembly {
            chainId := chainid()
        }
    }
}

File 3 of 18 : SafeMath.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.7.0;

/**
 * @dev Wrappers over Solidity's arithmetic operations with added overflow
 * checks.
 *
 * Arithmetic operations in Solidity wrap on overflow. This can easily result
 * in bugs, because programmers usually assume that an overflow raises an
 * error, which is the standard behavior in high level programming languages.
 * `SafeMath` restores this intuition by reverting the transaction when an
 * operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 */
library SafeMath {
    /**
     * @dev Returns the addition of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        uint256 c = a + b;
        if (c < a) return (false, 0);
        return (true, c);
    }

    /**
     * @dev Returns the substraction of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        if (b > a) return (false, 0);
        return (true, a - b);
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
        // benefit is lost if 'b' is also tested.
        // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
        if (a == 0) return (true, 0);
        uint256 c = a * b;
        if (c / a != b) return (false, 0);
        return (true, c);
    }

    /**
     * @dev Returns the division of two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        if (b == 0) return (false, 0);
        return (true, a / b);
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        if (b == 0) return (false, 0);
        return (true, a % b);
    }

    /**
     * @dev Returns the addition of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `+` operator.
     *
     * Requirements:
     *
     * - Addition cannot overflow.
     */
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        require(c >= a, "SafeMath: addition overflow");
        return c;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b <= a, "SafeMath: subtraction overflow");
        return a - b;
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `*` operator.
     *
     * Requirements:
     *
     * - Multiplication cannot overflow.
     */
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        if (a == 0) return 0;
        uint256 c = a * b;
        require(c / a == b, "SafeMath: multiplication overflow");
        return c;
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b > 0, "SafeMath: division by zero");
        return a / b;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b > 0, "SafeMath: modulo by zero");
        return a % b;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
     * overflow (when the result is negative).
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {trySub}.
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b <= a, errorMessage);
        return a - b;
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting with custom message on
     * division by zero. The result is rounded towards zero.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryDiv}.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b > 0, errorMessage);
        return a / b;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting with custom message when dividing by zero.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryMod}.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b > 0, errorMessage);
        return a % b;
    }
}

File 4 of 18 : ERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.7.0;

import "../../utils/Context.sol";
import "./IERC20.sol";
import "../../math/SafeMath.sol";

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

    mapping (address => uint256) private _balances;

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

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;
    uint8 private _decimals;

    /**
     * @dev Sets the values for {name} and {symbol}, initializes {decimals} with
     * a default value of 18.
     *
     * To select a different value for {decimals}, use {_setupDecimals}.
     *
     * All three of these values are immutable: they can only be set once during
     * construction.
     */
    constructor (string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
        _decimals = 18;
    }

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

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

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

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

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

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

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

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

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

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

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

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

        _beforeTokenTransfer(sender, recipient, amount);

        _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
        _balances[recipient] = _balances[recipient].add(amount);
        emit Transfer(sender, recipient, amount);
    }

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

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

        _totalSupply = _totalSupply.add(amount);
        _balances[account] = _balances[account].add(amount);
        emit Transfer(address(0), account, amount);
    }

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

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

        _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
        _totalSupply = _totalSupply.sub(amount);
        emit Transfer(account, address(0), amount);
    }

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

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

    /**
     * @dev Sets {decimals} to a value other than the default one of 18.
     *
     * WARNING: This function should only be called from the constructor. Most
     * applications that interact with token contracts will not expect
     * {decimals} to ever change, and may work incorrectly if it does.
     */
    function _setupDecimals(uint8 decimals_) internal virtual {
        _decimals = decimals_;
    }

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

File 5 of 18 : ERC20Burnable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.7.0;

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

/**
 * @dev Extension of {ERC20} that allows token holders to destroy both their own
 * tokens and those that they have an allowance for, in a way that can be
 * recognized off-chain (via event analysis).
 */
abstract contract ERC20Burnable is Context, ERC20 {
    using SafeMath for uint256;

    /**
     * @dev Destroys `amount` tokens from the caller.
     *
     * See {ERC20-_burn}.
     */
    function burn(uint256 amount) public virtual {
        _burn(_msgSender(), amount);
    }

    /**
     * @dev Destroys `amount` tokens from `account`, deducting from the caller's
     * allowance.
     *
     * See {ERC20-_burn} and {ERC20-allowance}.
     *
     * Requirements:
     *
     * - the caller must have allowance for ``accounts``'s tokens of at least
     * `amount`.
     */
    function burnFrom(address account, uint256 amount) public virtual {
        uint256 decreasedAllowance = allowance(account, _msgSender()).sub(amount, "ERC20: burn amount exceeds allowance");

        _approve(account, _msgSender(), decreasedAllowance);
        _burn(account, amount);
    }
}

File 6 of 18 : IERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.7.0;

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

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

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

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

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

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

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

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

File 7 of 18 : SafeERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.7.0;

import "./IERC20.sol";
import "../../math/SafeMath.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 SafeMath for uint256;
    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'
        // solhint-disable-next-line max-line-length
        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).add(value);
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero");
        _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
            // solhint-disable-next-line max-line-length
            require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
        }
    }
}

File 8 of 18 : Address.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.7.0;

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

        uint256 size;
        // solhint-disable-next-line no-inline-assembly
        assembly { size := extcodesize(account) }
        return size > 0;
    }

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

        // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
        (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");

        // solhint-disable-next-line avoid-low-level-calls
        (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");

        // solhint-disable-next-line avoid-low-level-calls
        (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");

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return _verifyCallResult(success, returndata, errorMessage);
    }

    function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private 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

                // solhint-disable-next-line no-inline-assembly
                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

File 9 of 18 : Context.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <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 GSN 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 payable) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes memory) {
        this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
        return msg.data;
    }
}

File 10 of 18 : EnumerableSet.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.7.0;

/**
 * @dev Library for managing
 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
 * types.
 *
 * Sets have the following properties:
 *
 * - Elements are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Elements are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```
 * contract Example {
 *     // Add the library methods
 *     using EnumerableSet for EnumerableSet.AddressSet;
 *
 *     // Declare a set state variable
 *     EnumerableSet.AddressSet private mySet;
 * }
 * ```
 *
 * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
 * and `uint256` (`UintSet`) are supported.
 */
library EnumerableSet {
    // To implement this library for multiple types with as little code
    // repetition as possible, we write it in terms of a generic Set type with
    // bytes32 values.
    // The Set implementation uses private functions, and user-facing
    // implementations (such as AddressSet) are just wrappers around the
    // underlying Set.
    // This means that we can only create new EnumerableSets for types that fit
    // in bytes32.

    struct Set {
        // Storage of set values
        bytes32[] _values;

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

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

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

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

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

            // When the value to delete is the last one, the swap operation is unnecessary. However, since this occurs
            // so rarely, we still do the swap anyway to avoid the gas cost of adding an 'if' statement.

            bytes32 lastvalue = set._values[lastIndex];

            // Move the last value to the index where the value to delete is
            set._values[toDeleteIndex] = lastvalue;
            // Update the index for the moved value
            set._indexes[lastvalue] = toDeleteIndex + 1; // All indexes are 1-based

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

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

            return true;
        } else {
            return false;
        }
    }

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

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

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

    // Bytes32Set

    struct Bytes32Set {
        Set _inner;
    }

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

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

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

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

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

    // AddressSet

    struct AddressSet {
        Set _inner;
    }

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

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

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

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

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


    // UintSet

    struct UintSet {
        Set _inner;
    }

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

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

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

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

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

File 11 of 18 : Pausable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.7.0;

import "./Context.sol";

/**
 * @dev Contract module which allows children to implement an emergency stop
 * mechanism that can be triggered by an authorized account.
 *
 * This module is used through inheritance. It will make available the
 * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
 * the functions of your contract. Note that they will not be pausable by
 * simply including this module, only once the modifiers are put in place.
 */
abstract contract Pausable is Context {
    /**
     * @dev Emitted when the pause is triggered by `account`.
     */
    event Paused(address account);

    /**
     * @dev Emitted when the pause is lifted by `account`.
     */
    event Unpaused(address account);

    bool private _paused;

    /**
     * @dev Initializes the contract in unpaused state.
     */
    constructor () {
        _paused = false;
    }

    /**
     * @dev Returns true if the contract is paused, and false otherwise.
     */
    function paused() public view virtual returns (bool) {
        return _paused;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is not paused.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    modifier whenNotPaused() {
        require(!paused(), "Pausable: paused");
        _;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is paused.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    modifier whenPaused() {
        require(paused(), "Pausable: not paused");
        _;
    }

    /**
     * @dev Triggers stopped state.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    function _pause() internal virtual whenNotPaused {
        _paused = true;
        emit Paused(_msgSender());
    }

    /**
     * @dev Returns to normal state.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    function _unpause() internal virtual whenPaused {
        _paused = false;
        emit Unpaused(_msgSender());
    }
}

File 12 of 18 : IDeCusSystem.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.6;
pragma experimental ABIEncoderV2;

import "@openzeppelin/contracts/utils/EnumerableSet.sol";

interface IDeCusSystem {
    struct Group {
        uint32 maxSatoshi;
        uint32 currSatoshi;
        uint32 nonce;
        uint32 required;
        EnumerableSet.AddressSet keeperSet;
    }

    enum Status {
        Available,
        DepositRequested,
        DepositReceived,
        WithdrawRequested
    }

    struct Receipt {
        string groupBtcAddress;
        string withdrawBtcAddress;
        bytes32 txId;
        uint32 amountInSatoshi;
        uint32 updateTimestamp;
        uint32 height;
        address recipient;
        Status status;
    }

    struct BtcRefundData {
        bytes32 txId;
        string groupBtcAddress;
        uint32 expiryTimestamp;
    }

    struct MintRequest {
        bytes32 receiptId;
        bytes32 txId;
        uint32 height;
    }

    // events
    event GroupAdded(string btcAddress, uint32 required, uint32 maxSatoshi, address[] keepers);
    event GroupDeleted(string btcAddress);
    event AllowKeeperExit(address indexed operator);
    event ToggleExitKeeper(address indexed keeper, bool isExit);

    event MintRequested(
        bytes32 indexed receiptId,
        address indexed recipient,
        uint32 amountInSatoshi,
        string groupBtcAddress
    );
    event MintRevoked(bytes32 indexed receiptId, string groupBtcAddress, address operator);
    event MintVerified(
        bytes32 indexed receiptId,
        string groupBtcAddress,
        address[] keepers,
        bytes32 btcTxId,
        uint32 btcTxHeight
    );
    event BurnRequested(
        bytes32 indexed receiptId,
        string groupBtcAddress,
        string withdrawBtcAddress,
        address operator
    );
    event BurnRevoked(
        bytes32 indexed receiptId,
        string groupBtcAddress,
        address recipient,
        address operator
    );
    event BurnVerified(bytes32 indexed receiptId, string groupBtcAddress, address operator);

    event Cooldown(address indexed keeper, uint32 endTime);

    event BtcRefunded(string groupBtcAddress, bytes32 txId, uint32 expiryTimestamp);
}

File 13 of 18 : IKeeperRegistry.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.6;
pragma experimental ABIEncoderV2;

interface IKeeperRegistry {
    struct KeeperData {
        uint256 amount;
        address asset;
        uint32 refCount;
        uint32 joinTimestamp;
    }

    function getCollateralWei(address keeper) external view returns (uint256);

    function isKeeperQualified(address keeper) external view returns (bool);

    function importKeepers(
        uint256 amount,
        address asset,
        address[] calldata keepers
    ) external;

    function addConfiscation(
        address sender,
        address asset,
        uint256 amount
    ) external;

    function incrementRefCount(address keeper) external;

    function decrementRefCount(address keeper) external;

    event MinCollateralUpdated(uint256 amount);

    event SystemUpdated(address oldSystem, address newSystem);
    event AssetAdded(address indexed asset);

    event KeeperAdded(address indexed keeper, address asset, uint256 amount);
    event KeeperDeleted(address indexed keeper, address asset, uint256 amount, uint256 cAmount);
    event KeeperImported(address indexed from, address asset, address[] keepers, uint256 amount);
    event KeeperAssetSwapped(address indexed keeper, address asset, uint256 amount);

    event KeeperRefCount(address indexed keeper, uint256 count);
    event KeeperPunished(address indexed keeper, address asset, uint256 collateral);

    event EarlyExitFeeBpsUpdated(uint8 bps);

    event LiquidationUpdated(address indexed previousLiquidation, address indexed newLiquidation);
    event Confiscated(address indexed liquidation, address asset, uint256 amount);
    event ConfiscationAdded(address asset, uint256 amount);
    event OverissueAdded(uint256 total, uint256 added);
    event OffsetOverissued(
        address indexed operator,
        uint256 satsAmount,
        uint256 remainingOverissueAmount
    );
}

File 14 of 18 : ISwapFee.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.6;
pragma experimental ABIEncoderV2;

interface ISwapFee {
    function getMintEthFee() external view returns (uint256);

    function getMintFeeAmount(uint256 amount) external view returns (uint256);

    function getBurnFeeAmount(uint256 amount) external view returns (uint256);

    function payMintEthFee() external payable;

    function payExtraMintFee(address from, uint256 amount) external returns (uint256);

    function payExtraBurnFee(address from, uint256 amount) external returns (uint256);

    event FeeCollected(address indexed to, address indexed asset, uint256 amount);
}

File 15 of 18 : ISwapRewarder.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.6;

interface ISwapRewarder {
    function mintReward(address to, uint256 amount) external;

    function burnReward(address to, uint256 amount) external;

    event SwapRewarded(address indexed to, uint256 amount, bool isMint);
}

File 16 of 18 : DeCusSystem.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.6;
pragma experimental ABIEncoderV2;

import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import "@openzeppelin/contracts/math/SafeMath.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/utils/Pausable.sol";
import "@openzeppelin/contracts/utils/EnumerableSet.sol";
import "@openzeppelin/contracts/drafts/EIP712.sol";

import {IDeCusSystem} from "../interfaces/IDeCusSystem.sol";
import {IKeeperRegistry} from "../interfaces/IKeeperRegistry.sol";
import {ISwapRewarder} from "../interfaces/ISwapRewarder.sol";
import {ISwapFee} from "../interfaces/ISwapFee.sol";
import {BtcUtility} from "../utils/BtcUtility.sol";
import {SATS} from "./SATS.sol";

contract DeCusSystem is AccessControl, Pausable, IDeCusSystem, EIP712("DeCus", "1.0") {
    using SafeMath for uint256;
    using EnumerableSet for EnumerableSet.AddressSet;
    using SafeERC20 for SATS;

    bytes32 public constant GROUP_ROLE = keccak256("GROUP_ROLE");
    bytes32 public constant GUARD_ROLE = keccak256("GUARD_ROLE");
    bytes32 private constant REQUEST_TYPEHASH =
        keccak256(abi.encodePacked("MintRequest(bytes32 receiptId,bytes32 txId,uint256 height)"));

    uint32 public constant KEEPER_COOLDOWN = 10 minutes;
    uint32 public constant WITHDRAW_VERIFICATION_END = 8 hours;
    uint32 public constant MINT_REQUEST_GRACE_PERIOD = 18 hours;
    uint32 public constant GROUP_REUSING_GAP = 30 minutes;
    uint32 public constant REFUND_GAP = 1 days;

    bool public keeperExitAllowed = false;
    SATS public sats;
    IKeeperRegistry public keeperRegistry;
    ISwapRewarder public rewarder;
    ISwapFee public fee;

    mapping(string => Group) private groups; // btc address -> Group
    mapping(bytes32 => Receipt) private receipts; // receipt ID -> Receipt
    mapping(address => uint32) public cooldownUntil; // keeper address -> cooldown end timestamp
    mapping(address => bool) public keeperExiting; // keeper -> exiting

    BtcRefundData private btcRefundData;

    modifier onlyAdmin() {
        require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), "require admin role");
        _;
    }

    modifier onlyGroupAdmin() {
        require(hasRole(GROUP_ROLE, msg.sender), "require group admin role");
        _;
    }

    modifier onlyGuard() {
        require(hasRole(GUARD_ROLE, msg.sender), "require guard role");
        _;
    }

    //================================= Public =================================
    constructor() {
        _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);

        _setupRole(GROUP_ROLE, msg.sender);

        _setupRole(GUARD_ROLE, msg.sender);
    }

    function initialize(
        SATS _sats,
        IKeeperRegistry _registry,
        ISwapRewarder _rewarder,
        ISwapFee _fee
    ) external onlyAdmin {
        sats = _sats;
        keeperRegistry = _registry;
        rewarder = _rewarder;
        fee = _fee;
    }

    // ------------------------------ keeper -----------------------------------
    function chill(address keeper, uint32 chillTime) external onlyGuard {
        _cooldown(keeper, _safeAdd(_blockTimestamp(), chillTime));
    }

    function allowKeeperExit() external onlyAdmin whenNotPaused {
        keeperExitAllowed = true;
        emit AllowKeeperExit(msg.sender);
    }

    function toggleExitKeeper() external whenNotPaused {
        require(keeperExitAllowed, "keeper exit not allowed");
        keeperExiting[msg.sender] = !keeperExiting[msg.sender];
        emit ToggleExitKeeper(msg.sender, keeperExiting[msg.sender]);
    }

    // -------------------------------- group ----------------------------------
    function getGroup(string calldata btcAddress)
        external
        view
        returns (
            uint32 required,
            uint32 maxSatoshi,
            uint32 currSatoshi,
            uint32 nonce,
            address[] memory keepers,
            bytes32 workingReceiptId
        )
    {
        Group storage group = groups[btcAddress];

        keepers = new address[](group.keeperSet.length());
        for (uint256 i = 0; i < group.keeperSet.length(); i++) {
            keepers[i] = group.keeperSet.at(i);
        }

        workingReceiptId = getReceiptId(btcAddress, group.nonce);
        required = group.required;
        maxSatoshi = group.maxSatoshi;
        currSatoshi = group.currSatoshi;
        nonce = group.nonce;
    }

    function addGroup(
        string calldata btcAddress,
        uint32 required,
        uint32 maxSatoshi,
        address[] calldata keepers
    ) external onlyGroupAdmin whenNotPaused {
        Group storage group = groups[btcAddress];
        require(group.maxSatoshi == 0, "group id already exist");

        group.required = required;
        group.maxSatoshi = maxSatoshi;
        for (uint256 i = 0; i < keepers.length; i++) {
            address keeper = keepers[i];
            require(keeperRegistry.isKeeperQualified(keeper), "keeper has insufficient collateral");
            group.keeperSet.add(keeper);
            keeperRegistry.incrementRefCount(keeper);
        }

        emit GroupAdded(btcAddress, required, maxSatoshi, keepers);
    }

    function deleteGroup(string calldata btcAddress) external whenNotPaused {
        Group storage group = groups[btcAddress];

        bool force = hasRole(DEFAULT_ADMIN_ROLE, msg.sender);
        require(
            force ||
                hasRole(GROUP_ROLE, msg.sender) ||
                (keeperExiting[msg.sender] && group.keeperSet.contains(msg.sender)),
            "not authorized"
        );

        _deleteGroup(btcAddress, group, force);
    }

    function deleteGroups(string[] calldata btcAddresses) external whenNotPaused {
        bool force = hasRole(DEFAULT_ADMIN_ROLE, msg.sender);
        bool isGroupAdmin = hasRole(GROUP_ROLE, msg.sender);
        for (uint256 i = 0; i < btcAddresses.length; i++) {
            string calldata btcAddress = btcAddresses[i];
            Group storage group = groups[btcAddress];
            require(
                force || isGroupAdmin || group.keeperSet.contains(msg.sender),
                "not authorized"
            );
            _deleteGroup(btcAddress, group, force);
        }
    }

    // ------------------------------- receipt ---------------------------------
    function getReceipt(bytes32 receiptId) external view returns (Receipt memory) {
        return receipts[receiptId];
    }

    function getReceiptId(string calldata groupBtcAddress, uint256 nonce)
        public
        pure
        returns (bytes32)
    {
        return keccak256(abi.encodePacked(groupBtcAddress, nonce));
    }

    function requestMint(
        string calldata groupBtcAddress,
        uint32 amountInSatoshi,
        uint32 nonce
    ) public payable whenNotPaused {
        require(amountInSatoshi > 0, "amount 0 is not allowed");
        fee.payMintEthFee{value: msg.value}();

        Group storage group = groups[groupBtcAddress];
        require(nonce == (group.nonce + 1), "invalid nonce");

        bytes32 prevReceiptId = getReceiptId(groupBtcAddress, group.nonce);
        Receipt storage prevReceipt = receipts[prevReceiptId];
        require(prevReceipt.status == Status.Available, "working receipt in progress");
        require(
            _blockTimestamp() > prevReceipt.updateTimestamp + GROUP_REUSING_GAP,
            "group cooling down"
        );
        delete receipts[prevReceiptId];

        group.nonce = nonce;

        require(
            (group.maxSatoshi - group.currSatoshi) == amountInSatoshi,
            "should fill all group allowance"
        );

        bytes32 receiptId = getReceiptId(groupBtcAddress, nonce);
        Receipt storage receipt = receipts[receiptId];
        _requestDeposit(receipt, groupBtcAddress, amountInSatoshi);

        emit MintRequested(receiptId, msg.sender, amountInSatoshi, groupBtcAddress);
    }

    function revokeMint(bytes32 receiptId) public {
        Receipt storage receipt = receipts[receiptId];
        require(receipt.recipient == msg.sender, "require receipt recipient");

        _revokeMint(receiptId, receipt);
    }

    function forceRequestMint(
        string calldata groupBtcAddress,
        uint32 amountInSatoshi,
        uint32 nonce
    ) public payable {
        bytes32 prevReceiptId = getReceiptId(groupBtcAddress, groups[groupBtcAddress].nonce);
        Receipt storage prevReceipt = receipts[prevReceiptId];

        _clearReceipt(prevReceipt, prevReceiptId);

        requestMint(groupBtcAddress, amountInSatoshi, nonce);
    }

    function verifyMint(
        MintRequest calldata request,
        address[] calldata keepers,
        bytes32[] calldata r,
        bytes32[] calldata s,
        uint256 packedV
    ) public whenNotPaused {
        Receipt storage receipt = receipts[request.receiptId];
        Group storage group = groups[receipt.groupBtcAddress];
        group.currSatoshi += receipt.amountInSatoshi;
        require(group.currSatoshi <= group.maxSatoshi, "amount exceed max allowance");

        _verifyMintRequest(group, request, keepers, r, s, packedV);
        _approveDeposit(receipt, request.txId, request.height);

        _mintSATS(receipt.recipient, receipt.amountInSatoshi);

        rewarder.mintReward(receipt.recipient, receipt.amountInSatoshi);

        emit MintVerified(
            request.receiptId,
            receipt.groupBtcAddress,
            keepers,
            request.txId,
            request.height
        );
    }

    function requestBurn(bytes32 receiptId, string calldata withdrawBtcAddress)
        public
        whenNotPaused
    {
        Receipt storage receipt = receipts[receiptId];

        _requestWithdraw(receipt, withdrawBtcAddress);

        _paySATSForBurn(msg.sender, address(this), receipt.amountInSatoshi);

        emit BurnRequested(receiptId, receipt.groupBtcAddress, withdrawBtcAddress, msg.sender);
    }

    function verifyBurn(bytes32 receiptId) public whenNotPaused {
        Receipt storage receipt = receipts[receiptId];
        require(msg.sender == receipt.recipient, "only recipient");

        _approveWithdraw(receipt);

        _burnSATS(receipt.amountInSatoshi);

        Group storage group = groups[receipt.groupBtcAddress];
        group.currSatoshi -= receipt.amountInSatoshi;

        if (rewarder != ISwapRewarder(0))
            rewarder.burnReward(receipt.recipient, receipt.amountInSatoshi);

        emit BurnVerified(receiptId, receipt.groupBtcAddress, msg.sender);
    }

    function recoverBurn(bytes32 receiptId) public onlyAdmin {
        Receipt storage receipt = receipts[receiptId];

        _revokeWithdraw(receipt);

        _refundSATSForBurn(receipt.recipient, receipt.amountInSatoshi);

        emit BurnRevoked(receiptId, receipt.groupBtcAddress, receipt.recipient, msg.sender);
    }

    // -------------------------------- BTC refund -----------------------------------
    function getRefundData() external view returns (BtcRefundData memory) {
        return btcRefundData;
    }

    function refundBtc(string calldata groupBtcAddress, bytes32 txId)
        public
        onlyAdmin
        whenNotPaused
    {
        bytes32 receiptId = getReceiptId(groupBtcAddress, groups[groupBtcAddress].nonce);
        Receipt storage receipt = receipts[receiptId];

        _clearReceipt(receipt, receiptId);

        require(receipt.status == Status.Available, "receipt not in available state");
        require(btcRefundData.expiryTimestamp < _blockTimestamp(), "refund cool down");

        uint32 expiryTimestamp = _blockTimestamp() + REFUND_GAP;
        btcRefundData.expiryTimestamp = expiryTimestamp;
        btcRefundData.txId = txId;
        btcRefundData.groupBtcAddress = groupBtcAddress;

        emit BtcRefunded(groupBtcAddress, txId, expiryTimestamp);
    }

    // -------------------------------- Pausable -----------------------------------
    function pause() public onlyGuard {
        _pause();
    }

    function unpause() public onlyGuard {
        _unpause();
    }

    //=============================== Private ==================================
    // ------------------------------ keeper -----------------------------------
    function _cooldown(address keeper, uint32 cooldownEnd) private {
        cooldownUntil[keeper] = cooldownEnd;
        emit Cooldown(keeper, cooldownEnd);
    }

    function _clearReceipt(Receipt storage receipt, bytes32 receiptId) private {
        if (receipt.status == Status.WithdrawRequested) {
            require(
                _blockTimestamp() > receipt.updateTimestamp + WITHDRAW_VERIFICATION_END,
                "withdraw in progress"
            );
            _forceVerifyBurn(receiptId, receipt);
        } else if (receipt.status == Status.DepositRequested) {
            require(
                _blockTimestamp() > receipt.updateTimestamp + MINT_REQUEST_GRACE_PERIOD,
                "deposit in progress"
            );
            _forceRevokeMint(receiptId, receipt);
        }
    }

    // -------------------------------- group ----------------------------------
    function _deleteGroup(
        string calldata btcAddress,
        Group storage group,
        bool force
    ) private {
        bytes32 receiptId = getReceiptId(btcAddress, group.nonce);
        Receipt storage receipt = receipts[receiptId];
        require(
            (receipt.status != Status.Available) ||
                (_blockTimestamp() > receipt.updateTimestamp + GROUP_REUSING_GAP),
            "receipt in resuing gap"
        );

        _clearReceipt(receipt, receiptId);
        if ((force && (receipt.status == Status.DepositReceived))) {
            receipt.status = Status.Available;
            group.currSatoshi = 0;
        }
        delete receipts[receiptId];

        for (uint256 i = 0; i < group.keeperSet.length(); i++) {
            address keeper = group.keeperSet.at(i);
            keeperRegistry.decrementRefCount(keeper);
        }

        require(group.currSatoshi == 0, "group balance > 0");
        delete groups[btcAddress];
        emit GroupDeleted(btcAddress);
    }

    function _revokeMint(bytes32 receiptId, Receipt storage receipt) private {
        _revokeDeposit(receipt);

        emit MintRevoked(receiptId, receipt.groupBtcAddress, msg.sender);
    }

    function _forceRevokeMint(bytes32 receiptId, Receipt storage receipt) private {
        receipt.status = Status.Available;

        emit MintRevoked(receiptId, receipt.groupBtcAddress, msg.sender);
    }

    function _verifyMintRequest(
        Group storage group,
        MintRequest calldata request,
        address[] calldata keepers,
        bytes32[] calldata r,
        bytes32[] calldata s,
        uint256 packedV
    ) private {
        require(keepers.length >= group.required, "not enough keepers");

        uint32 cooldownTime = _blockTimestamp() + KEEPER_COOLDOWN;
        bytes32 digest = _hashTypedDataV4(
            keccak256(abi.encode(REQUEST_TYPEHASH, request.receiptId, request.txId, request.height))
        );

        for (uint256 i = 0; i < keepers.length; i++) {
            address keeper = keepers[i];

            require(cooldownUntil[keeper] <= _blockTimestamp(), "keeper is in cooldown");
            require(group.keeperSet.contains(keeper), "keeper is not in group");
            require(ecrecover(digest, uint8(packedV), r[i], s[i]) == keeper, "invalid signature");
            // assert keepers.length <= 32
            packedV >>= 8;

            _cooldown(keeper, cooldownTime);
        }
    }

    function _forceVerifyBurn(bytes32 receiptId, Receipt storage receipt) private {
        receipt.status = Status.Available;

        _burnSATS(receipt.amountInSatoshi);

        Group storage group = groups[receipt.groupBtcAddress];
        group.currSatoshi -= receipt.amountInSatoshi;

        emit BurnVerified(receiptId, receipt.groupBtcAddress, msg.sender);
    }

    // ------------------------------- receipt ---------------------------------
    function _requestDeposit(
        Receipt storage receipt,
        string calldata groupBtcAddress,
        uint32 amountInSatoshi
    ) private {
        require(receipt.status == Status.Available, "receipt is not in Available state");

        receipt.groupBtcAddress = groupBtcAddress;
        receipt.recipient = msg.sender;
        receipt.amountInSatoshi = amountInSatoshi;
        receipt.updateTimestamp = _blockTimestamp();
        receipt.status = Status.DepositRequested;
    }

    function _approveDeposit(
        Receipt storage receipt,
        bytes32 txId,
        uint32 height
    ) private {
        require(
            receipt.status == Status.DepositRequested,
            "receipt is not in DepositRequested state"
        );

        receipt.txId = txId;
        receipt.height = height;
        receipt.status = Status.DepositReceived;
    }

    function _revokeDeposit(Receipt storage receipt) private {
        require(receipt.status == Status.DepositRequested, "receipt is not DepositRequested");

        _markFinish(receipt);
    }

    function _requestWithdraw(Receipt storage receipt, string calldata withdrawBtcAddress) private {
        require(
            receipt.status == Status.DepositReceived,
            "receipt is not in DepositReceived state"
        );

        receipt.recipient = msg.sender;
        receipt.withdrawBtcAddress = withdrawBtcAddress;
        receipt.updateTimestamp = _blockTimestamp();
        receipt.status = Status.WithdrawRequested;
    }

    function _approveWithdraw(Receipt storage receipt) private {
        require(
            receipt.status == Status.WithdrawRequested,
            "receipt is not in withdraw requested status"
        );

        _markFinish(receipt);
    }

    function _revokeWithdraw(Receipt storage receipt) private {
        require(
            receipt.status == Status.WithdrawRequested,
            "receipt is not in WithdrawRequested status"
        );

        receipt.status = Status.DepositReceived;
    }

    function _markFinish(Receipt storage receipt) private {
        receipt.updateTimestamp = _blockTimestamp();
        receipt.status = Status.Available;
    }

    function _blockTimestamp() internal view virtual returns (uint32) {
        return uint32(block.timestamp);
    }

    function _safeAdd(uint32 x, uint32 y) internal pure returns (uint32 z) {
        require((z = x + y) >= x);
    }

    // -------------------------------- SATS -----------------------------------
    function _mintSATS(address to, uint256 amountInSatoshi) private {
        uint256 amount = (amountInSatoshi).mul(BtcUtility.getSatsAmountMultiplier());

        uint256 feeAmount = fee.payExtraMintFee(to, amount);
        if (feeAmount > 0) {
            sats.mint(address(fee), feeAmount);
            sats.mint(to, amount.sub(feeAmount));
        } else {
            sats.mint(to, amount);
        }
    }

    function _burnSATS(uint256 amountInSatoshi) private {
        uint256 amount = (amountInSatoshi).mul(BtcUtility.getSatsAmountMultiplier());

        sats.burn(amount);
    }

    // user transfer SATS when requestBurn
    function _paySATSForBurn(
        address from,
        address to,
        uint256 amountInSatoshi
    ) private {
        uint256 amount = (amountInSatoshi).mul(BtcUtility.getSatsAmountMultiplier());

        uint256 feeAmount = fee.payExtraBurnFee(from, amount);

        sats.safeTransferFrom(from, to, amount.add(feeAmount));

        if (feeAmount > 0) {
            sats.safeTransfer(address(fee), feeAmount);
        }
    }

    // refund user when recoverBurn
    function _refundSATSForBurn(address to, uint256 amountInSatoshi) private {
        // fee is not refunded
        uint256 amount = (amountInSatoshi).mul(BtcUtility.getSatsAmountMultiplier());

        sats.safeTransfer(to, amount);
    }
}

File 17 of 18 : SATS.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.6;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20Burnable.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";

contract SATS is AccessControl, ERC20Burnable {
    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");

    constructor() ERC20("DeCus Satoshi", "SATS") {
        _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);

        _setupDecimals(10);
    }

    function mint(address to, uint256 amount) public {
        require(hasRole(MINTER_ROLE, msg.sender), "require minter role");
        _mint(to, amount);
    }
}

File 18 of 18 : BtcUtility.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;

library BtcUtility {
    uint256 public constant ERC20_DECIMAL = 18;
    uint256 public constant SATOSHI_DECIMAL = 8;

    function getSatsAmountMultiplier() internal pure returns (uint256) {
        return 1e10;
    }

    function getWeiMultiplier(uint256 decimal) internal pure returns (uint256) {
        require(ERC20_DECIMAL >= decimal, "asset decimal not supported");

        // the result is strictly <= 10**18, no need to check overflow
        return 10**uint256(ERC20_DECIMAL - decimal);
    }

    function getSatoshiDivisor(uint256 decimal) internal pure returns (uint256) {
        require((SATOSHI_DECIMAL <= decimal) && (decimal <= 18), "asset decimal not supported");

        // the result is strictly <= 10**10, no need to check overflow
        return 10**uint256(decimal - SATOSHI_DECIMAL);
    }
}

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

Contract Security Audit

Contract ABI

[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"}],"name":"AllowKeeperExit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"groupBtcAddress","type":"string"},{"indexed":false,"internalType":"bytes32","name":"txId","type":"bytes32"},{"indexed":false,"internalType":"uint32","name":"expiryTimestamp","type":"uint32"}],"name":"BtcRefunded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"receiptId","type":"bytes32"},{"indexed":false,"internalType":"string","name":"groupBtcAddress","type":"string"},{"indexed":false,"internalType":"string","name":"withdrawBtcAddress","type":"string"},{"indexed":false,"internalType":"address","name":"operator","type":"address"}],"name":"BurnRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"receiptId","type":"bytes32"},{"indexed":false,"internalType":"string","name":"groupBtcAddress","type":"string"},{"indexed":false,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"address","name":"operator","type":"address"}],"name":"BurnRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"receiptId","type":"bytes32"},{"indexed":false,"internalType":"string","name":"groupBtcAddress","type":"string"},{"indexed":false,"internalType":"address","name":"operator","type":"address"}],"name":"BurnVerified","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"keeper","type":"address"},{"indexed":false,"internalType":"uint32","name":"endTime","type":"uint32"}],"name":"Cooldown","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"btcAddress","type":"string"},{"indexed":false,"internalType":"uint32","name":"required","type":"uint32"},{"indexed":false,"internalType":"uint32","name":"maxSatoshi","type":"uint32"},{"indexed":false,"internalType":"address[]","name":"keepers","type":"address[]"}],"name":"GroupAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"btcAddress","type":"string"}],"name":"GroupDeleted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"receiptId","type":"bytes32"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint32","name":"amountInSatoshi","type":"uint32"},{"indexed":false,"internalType":"string","name":"groupBtcAddress","type":"string"}],"name":"MintRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"receiptId","type":"bytes32"},{"indexed":false,"internalType":"string","name":"groupBtcAddress","type":"string"},{"indexed":false,"internalType":"address","name":"operator","type":"address"}],"name":"MintRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"receiptId","type":"bytes32"},{"indexed":false,"internalType":"string","name":"groupBtcAddress","type":"string"},{"indexed":false,"internalType":"address[]","name":"keepers","type":"address[]"},{"indexed":false,"internalType":"bytes32","name":"btcTxId","type":"bytes32"},{"indexed":false,"internalType":"uint32","name":"btcTxHeight","type":"uint32"}],"name":"MintVerified","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","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":"keeper","type":"address"},{"indexed":false,"internalType":"bool","name":"isExit","type":"bool"}],"name":"ToggleExitKeeper","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"GROUP_REUSING_GAP","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"GROUP_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"GUARD_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"KEEPER_COOLDOWN","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MINT_REQUEST_GRACE_PERIOD","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"REFUND_GAP","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"WITHDRAW_VERIFICATION_END","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"btcAddress","type":"string"},{"internalType":"uint32","name":"required","type":"uint32"},{"internalType":"uint32","name":"maxSatoshi","type":"uint32"},{"internalType":"address[]","name":"keepers","type":"address[]"}],"name":"addGroup","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"allowKeeperExit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"keeper","type":"address"},{"internalType":"uint32","name":"chillTime","type":"uint32"}],"name":"chill","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"cooldownUntil","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"btcAddress","type":"string"}],"name":"deleteGroup","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string[]","name":"btcAddresses","type":"string[]"}],"name":"deleteGroups","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"fee","outputs":[{"internalType":"contract ISwapFee","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"groupBtcAddress","type":"string"},{"internalType":"uint32","name":"amountInSatoshi","type":"uint32"},{"internalType":"uint32","name":"nonce","type":"uint32"}],"name":"forceRequestMint","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"string","name":"btcAddress","type":"string"}],"name":"getGroup","outputs":[{"internalType":"uint32","name":"required","type":"uint32"},{"internalType":"uint32","name":"maxSatoshi","type":"uint32"},{"internalType":"uint32","name":"currSatoshi","type":"uint32"},{"internalType":"uint32","name":"nonce","type":"uint32"},{"internalType":"address[]","name":"keepers","type":"address[]"},{"internalType":"bytes32","name":"workingReceiptId","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"receiptId","type":"bytes32"}],"name":"getReceipt","outputs":[{"components":[{"internalType":"string","name":"groupBtcAddress","type":"string"},{"internalType":"string","name":"withdrawBtcAddress","type":"string"},{"internalType":"bytes32","name":"txId","type":"bytes32"},{"internalType":"uint32","name":"amountInSatoshi","type":"uint32"},{"internalType":"uint32","name":"updateTimestamp","type":"uint32"},{"internalType":"uint32","name":"height","type":"uint32"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"enum IDeCusSystem.Status","name":"status","type":"uint8"}],"internalType":"struct IDeCusSystem.Receipt","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"groupBtcAddress","type":"string"},{"internalType":"uint256","name":"nonce","type":"uint256"}],"name":"getReceiptId","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getRefundData","outputs":[{"components":[{"internalType":"bytes32","name":"txId","type":"bytes32"},{"internalType":"string","name":"groupBtcAddress","type":"string"},{"internalType":"uint32","name":"expiryTimestamp","type":"uint32"}],"internalType":"struct IDeCusSystem.BtcRefundData","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getRoleMember","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleMemberCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"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":"contract SATS","name":"_sats","type":"address"},{"internalType":"contract IKeeperRegistry","name":"_registry","type":"address"},{"internalType":"contract ISwapRewarder","name":"_rewarder","type":"address"},{"internalType":"contract ISwapFee","name":"_fee","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"keeperExitAllowed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"keeperExiting","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"keeperRegistry","outputs":[{"internalType":"contract IKeeperRegistry","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"receiptId","type":"bytes32"}],"name":"recoverBurn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"groupBtcAddress","type":"string"},{"internalType":"bytes32","name":"txId","type":"bytes32"}],"name":"refundBtc","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"receiptId","type":"bytes32"},{"internalType":"string","name":"withdrawBtcAddress","type":"string"}],"name":"requestBurn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"groupBtcAddress","type":"string"},{"internalType":"uint32","name":"amountInSatoshi","type":"uint32"},{"internalType":"uint32","name":"nonce","type":"uint32"}],"name":"requestMint","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"receiptId","type":"bytes32"}],"name":"revokeMint","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":[],"name":"rewarder","outputs":[{"internalType":"contract ISwapRewarder","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"sats","outputs":[{"internalType":"contract SATS","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"toggleExitKeeper","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"receiptId","type":"bytes32"}],"name":"verifyBurn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32","name":"receiptId","type":"bytes32"},{"internalType":"bytes32","name":"txId","type":"bytes32"},{"internalType":"uint32","name":"height","type":"uint32"}],"internalType":"struct IDeCusSystem.MintRequest","name":"request","type":"tuple"},{"internalType":"address[]","name":"keepers","type":"address[]"},{"internalType":"bytes32[]","name":"r","type":"bytes32[]"},{"internalType":"bytes32[]","name":"s","type":"bytes32[]"},{"internalType":"uint256","name":"packedV","type":"uint256"}],"name":"verifyMint","outputs":[],"stateMutability":"nonpayable","type":"function"}]

6101206040526001805461ff00191690553480156200001d57600080fd5b506040805180820182526005815264446543757360d81b6020808301918252835180850190945260038452620312e360ec1b908401526001805460ff191690558151902060c08190527fe6bbd6277e1bf288eed5e8d1780f9a50b239e86b153736bceebccf4ea79d90b360e08190529192917f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f620000ba62000143565b60a052620000ca81848462000147565b6080526101005250620000e5925060009150339050620001ab565b620001117f8d6b526d6b13eab5a67762424267752264b236dd46b48a705b5b5249049123f733620001ab565b6200013d7f043c983c49d46f0e102151eaf8085d4a2e6571d5df2d47b013f39bddfd4a639d33620001ab565b620002bf565b4690565b60008383836200015662000143565b3060405160200180868152602001858152602001848152602001838152602001826001600160a01b03168152602001955050505050506040516020818303038152906040528051906020012090509392505050565b620001b78282620001bb565b5050565b600082815260208181526040909120620001e091839062001fb462000234821b17901c565b15620001b757620001f062000254565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b60006200024b836001600160a01b03841662000258565b90505b92915050565b3390565b6000620002668383620002a7565b6200029e575081546001818101845560008481526020808220909301849055845484825282860190935260409020919091556200024e565b5060006200024e565b60009081526001919091016020526040902054151590565b60805160a05160c05160e05161010051614b54620002ff600039806133405250806133825250806133615250806132e75250806133175250614b546000f3fe6080604052600436106102675760003560e01c80637a5c8f4711610144578063aa57665b116100b6578063d547741f1161007a578063d547741f146106b5578063dcc3e06e146106d5578063ddca3f43146106ea578063e8c86b03146106ff578063f8c8765e1461071f578063fcecbb611461073f57610267565b8063aa57665b1461061b578063abef281e1461062e578063b94cd07f14610660578063c812a21514610680578063ca15c8731461069557610267565b80639010d07c116101085780639010d07c1461057157806391d14854146105915780639fd72189146105b1578063a217fddf146105d1578063a2ad4d9b146105e6578063a965476d1461060657610267565b80637a5c8f47146104fb5780637f7bcb621461051d578063833eeb571461053257806383e22774146105475780638456cb591461055c57610267565b80633f4ba83a116101dd5780635cabd667116101a15780635cabd667146104695780635f890657146104895780636557f366146104a95780636e157869146104be5780637188ac00146104d357806372df0e38146104e857610267565b80633f4ba83a146103ea5780634327c083146103ff57806348a4a4891461041f578063507d87e4146104345780635c975abb1461045457610267565b80632f2ff15d1161022f5780632f2ff15d1461031b5780632f7f636a1461033b57806336568abe14610368578063372fedf11461038857806338f39b48146103a85780633d701d00146103ca57610267565b806303ed0ee51461026c5780631aa8517c14610297578063248a9ca3146102ae57806325d40e3c146102ce5780632e8b92a9146102fb575b600080fd5b34801561027857600080fd5b5061028161076c565b60405161028e9190613f9e565b60405180910390f35b3480156102a357600080fd5b506102ac61077e565b005b3480156102ba57600080fd5b506102816102c9366004613998565b610857565b3480156102da57600080fd5b506102ee6102e93660046138e8565b61086f565b60405161028e919061489b565b34801561030757600080fd5b506102ac610316366004613aa5565b610887565b34801561032757600080fd5b506102ac6103363660046139b0565b610981565b34801561034757600080fd5b5061035b6103563660046138e8565b6109e8565b60405161028e9190613f93565b34801561037457600080fd5b506102ac6103833660046139b0565b6109fd565b34801561039457600080fd5b506102ac6103a3366004613998565b610a5e565b3480156103b457600080fd5b506103bd610c2c565b60405161028e91906147ac565b3480156103d657600080fd5b506102ac6103e5366004613904565b610cf2565b3480156103f657600080fd5b506102ac610d40565b34801561040b57600080fd5b506102ac61041a366004613c17565b610d7e565b34801561042b57600080fd5b506102ee610f8c565b34801561044057600080fd5b506102ac61044f366004613998565b610f92565b34801561046057600080fd5b5061035b610fdd565b34801561047557600080fd5b506102ac610484366004613938565b610fe6565b34801561049557600080fd5b506102ac6104a43660046139df565b611107565b3480156104b557600080fd5b506102ee6111cc565b3480156104ca57600080fd5b506102ee6111d2565b3480156104df57600080fd5b5061035b6111d8565b6102ac6104f6366004613b23565b6111e6565b34801561050757600080fd5b50610510611251565b60405161028e9190613f47565b34801561052957600080fd5b506102ee611266565b34801561053e57600080fd5b506102ee61126c565b34801561055357600080fd5b50610510611273565b34801561056857600080fd5b506102ac611282565b34801561057d57600080fd5b5061051061058c366004613a29565b6112be565b34801561059d57600080fd5b5061035b6105ac3660046139b0565b6112df565b3480156105bd57600080fd5b506102ac6105cc366004613ad9565b6112f7565b3480156105dd57600080fd5b50610281611487565b3480156105f257600080fd5b50610281610601366004613ad9565b61148c565b34801561061257600080fd5b506102ac6114c3565b6102ac610629366004613b23565b611573565b34801561063a57600080fd5b5061064e610649366004613aa5565b611863565b60405161028e969594939291906148d5565b34801561066c57600080fd5b506102ac61067b366004613b86565b611982565b34801561068c57600080fd5b50610281611c0c565b3480156106a157600080fd5b506102816106b0366004613998565b611c1e565b3480156106c157600080fd5b506102ac6106d03660046139b0565b611c35565b3480156106e157600080fd5b50610510611c8e565b3480156106f657600080fd5b50610510611c9d565b34801561070b57600080fd5b506102ac61071a366004613998565b611cac565b34801561072b57600080fd5b506102ac61073a366004613a4a565b611d69565b34801561074b57600080fd5b5061075f61075a366004613998565b611deb565b60405161028e91906147ee565b600080516020614a0f83398151915281565b610786610fdd565b156107cb576040805162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b604482015290519081900360640190fd5b600154610100900460ff166107fb5760405162461bcd60e51b81526004016107f2906146f3565b60405180910390fd5b3360008181526008602052604090819020805460ff19811660ff91821615179182905591517f83cc6c1fe93786e75c247528820fb8e5def97df72d6233ff0afaad2b46e54a219261084d921690613f93565b60405180910390a2565b6000818152602081905260409020600201545b919050565b60076020526000908152604090205463ffffffff1681565b61088f610fdd565b156108d4576040805162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b604482015290519081900360640190fd5b6000600583836040516108e8929190613e63565b9081526040519081900360200190209050600061090581336112df565b905080806109265750610926600080516020614aa6833981519152336112df565b8061095357503360009081526008602052604090205460ff16801561095357506109536001830133611fc9565b61096f5760405162461bcd60e51b81526004016107f2906144f2565b61097b84848484611fde565b50505050565b60008281526020819052604090206002015461099f906105ac61223e565b6109da5760405162461bcd60e51b815260040180806020018281038252602f8152602001806149e0602f913960400191505060405180910390fd5b6109e48282612242565b5050565b60086020526000908152604090205460ff1681565b610a0561223e565b6001600160a01b0316816001600160a01b031614610a545760405162461bcd60e51b815260040180806020018281038252602f815260200180614af0602f913960400191505060405180910390fd5b6109e482826122ab565b610a66610fdd565b15610aab576040805162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b604482015290519081900360640190fd5b60008181526006602052604090206003810154600160601b90046001600160a01b03163314610aec5760405162461bcd60e51b81526004016107f290614251565b610af581612314565b6003810154610b099063ffffffff16612352565b6000600582600001604051610b1e9190613e88565b908152604051602091819003919091019020600383810154825463ffffffff60201b19811663ffffffff928316600160201b9283900484160390921602178255549091506001600160a01b031615610bea5760038054908301546040516346749cd360e01b81526001600160a01b03928316926346749cd392610bb792600160601b82049092169163ffffffff90911690600401613f74565b600060405180830381600087803b158015610bd157600080fd5b505af1158015610be5573d6000803e3d6000fd5b505050505b827fd90232357c1ad1e207410849ce0a0374bac4c22771453838f285ceca745547e38360000133604051610c1f929190614073565b60405180910390a2505050565b610c346136e0565b60408051606081018252600980548252600a80548451602060026001841615610100026000190190931692909204601f81018390048302820183019096528581529394929381860193909291830182828015610cd15780601f10610ca657610100808354040283529160200191610cd1565b820191906000526020600020905b815481529060010190602001808311610cb457829003601f168201915b50505091835250506002919091015463ffffffff1660209091015290505b90565b610d0a600080516020614a0f833981519152336112df565b610d265760405162461bcd60e51b81526004016107f290614572565b6109e482610d3b610d356123cb565b846123cf565b6123e8565b610d58600080516020614a0f833981519152336112df565b610d745760405162461bcd60e51b81526004016107f290614572565b610d7c612443565b565b610d86610fdd565b15610dcb576040805162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b604482015290519081900360640190fd5b87356000908152600660205260408082209051909190600590610def908490613e88565b9081526040516020918190039190910190206003830154815463ffffffff60201b19811663ffffffff928316600160201b9283900484160183168202178084559293508282169204161115610e565760405162461bcd60e51b81526004016107f29061421a565b610e67818b8b8b8b8b8b8b8b6124e3565b610e858260208c0135610e8060608e0160408f01613ce4565b612705565b6003820154610eaa906001600160a01b03600160601b8204169063ffffffff16612777565b6003805490830154604051634d24848760e11b81526001600160a01b0392831692639a49090e92610ef192600160601b82049092169163ffffffff90911690600401613f74565b600060405180830381600087803b158015610f0b57600080fd5b505af1158015610f1f573d6000803e3d6000fd5b5050505089600001357f19d0f59e71cb6f748028c86f27cb22d05c57315a9c850320ecda2cd13f5a43d5836000018b8b8e602001358f6040016020810190610f679190613ce4565b604051610f789594939291906140d0565b60405180910390a250505050505050505050565b61fd2081565b60008181526006602052604090206003810154600160601b90046001600160a01b03163314610fd35760405162461bcd60e51b81526004016107f2906146bc565b6109e4828261296a565b60015460ff1690565b610fee610fdd565b15611033576040805162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b604482015290519081900360640190fd5b600061103f81336112df565b9050600061105b600080516020614aa6833981519152336112df565b905060005b838110156111005736600086868481811061107757fe5b90506020028101906110899190614957565b915091506000600583836040516110a1929190613e63565b9081526020016040518091039020905085806110ba5750845b806110cd57506110cd6001820133611fc9565b6110e95760405162461bcd60e51b81526004016107f2906144f2565b6110f583838389611fde565b505050600101611060565b5050505050565b61110f610fdd565b15611154576040805162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b604482015290519081900360640190fd5b600083815260066020526040902061116d8184846129a8565b6003810154611185903390309063ffffffff16612a4a565b837ff0e638df6d296aaddeb18409852c4fa659a87b113a1fdb9cf7535668bd49497d826000018585336040516111be9493929190614115565b60405180910390a250505050565b61708081565b61070881565b600154610100900460ff1681565b60006112228585600588886040516111ff929190613e63565b9081526040519081900360200190205463ffffffff600160401b9091041661148c565b600081815260066020526040902090915061123d8183612b34565b61124986868686611573565b505050505050565b6001546201000090046001600160a01b031681565b61025881565b6201518081565b6002546001600160a01b031681565b61129a600080516020614a0f833981519152336112df565b6112b65760405162461bcd60e51b81526004016107f290614572565b610d7c612c0b565b60008281526020819052604081206112d69083612c8d565b90505b92915050565b60008281526020819052604081206112d69083611fc9565b6113026000336112df565b61131e5760405162461bcd60e51b81526004016107f290614473565b611326610fdd565b1561136b576040805162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b604482015290519081900360640190fd5b60006113848484600587876040516111ff929190613e63565b600081815260066020526040902090915061139f8183612b34565b6000600482015460ff1660038111156113b457fe5b146113d15760405162461bcd60e51b81526004016107f2906141e3565b6113d96123cb565b600b5463ffffffff9182169116106114035760405162461bcd60e51b81526004016107f2906145cc565b6000620151806114116123cb565b600b805463ffffffff19169190920163ffffffff81169190911790915560098590559050611441600a878761370a565b507f8588e607efd130a12b8af9ae3c9f09eb4e81b7976778146783cfa8cdc3799594868686846040516114779493929190613ffa565b60405180910390a1505050505050565b600081565b60008383836040516020016114a393929190613e73565b6040516020818303038152906040528051906020012090505b9392505050565b6114ce6000336112df565b6114ea5760405162461bcd60e51b81526004016107f290614473565b6114f2610fdd565b15611537576040805162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b604482015290519081900360640190fd5b6001805461ff00191661010017905560405133907f0ff3bf87a808983b14400cade429c3e8e3ee8459a32a33de6f3f8b1cd2845dea90600090a2565b61157b610fdd565b156115c0576040805162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b604482015290519081900360640190fd5b60008263ffffffff16116115e65760405162461bcd60e51b81526004016107f29061472a565b6004805460408051637c9b167b60e11b815290516001600160a01b039092169263f9362cf6923492808301926000929182900301818588803b15801561162b57600080fd5b505af115801561163f573d6000803e3d6000fd5b5050505050600060058585604051611658929190613e63565b908152602001604051809103902090508060000160089054906101000a900463ffffffff1660010163ffffffff168263ffffffff16146116aa5760405162461bcd60e51b81526004016107f2906144cb565b80546000906116c99087908790600160401b900463ffffffff1661148c565b6000818152600660205260408120919250600482015460ff1660038111156116ed57fe5b1461170a5760405162461bcd60e51b81526004016107f290614625565b600381015463ffffffff600160201b9091048116610708011661172b6123cb565b63ffffffff161161174e5760405162461bcd60e51b81526004016107f290614180565b6000828152600660205260408120906117678282613796565b611775600183016000613796565b506000600282018190556003820155600401805460ff19169055825463ffffffff858116600160401b0263ffffffff60401b199290921691909117808555600160201b81048216908216038116908616146117e25760405162461bcd60e51b81526004016107f290614279565b60006117f588888763ffffffff1661148c565b6000818152600660205260409020909150611812818a8a8a612c99565b336001600160a01b0316827f93e399f7caab186cd9c637476e9d46c57d7e68ecb5392570123578f392d5e22c898c8c604051611850939291906148ac565b60405180910390a3505050505050505050565b600080600080606060008060058989604051611880929190613e63565b9081526020016040518091039020905061189c81600101612d46565b67ffffffffffffffff811180156118b257600080fd5b506040519080825280602002602001820160405280156118dc578160200160208202803683370190505b50925060005b6118ee82600101612d46565b81101561192e576119026001830182612c8d565b84828151811061190e57fe5b6001600160a01b03909216602092830291909101909101526001016118e2565b50805461194b908a908a90600160401b900463ffffffff1661148c565b905463ffffffff600160601b820481169b8183169b50600160201b830482169a50600160401b909204169750929550935090915050565b61199a600080516020614aa6833981519152336112df565b6119b65760405162461bcd60e51b81526004016107f2906141ac565b6119be610fdd565b15611a03576040805162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b604482015290519081900360640190fd5b600060058787604051611a17929190613e63565b908152604051908190036020019020805490915063ffffffff1615611a4e5760405162461bcd60e51b81526004016107f29061436a565b805463ffffffff85811663ffffffff19918816600160601b0263ffffffff60601b19909316929092171617815560005b82811015611bc1576000848483818110611a9457fe5b9050602002016020810190611aa991906138e8565b600254604051635ed55fc160e11b81529192506001600160a01b03169063bdaabf8290611ada908490600401613f47565b60206040518083038186803b158015611af257600080fd5b505afa158015611b06573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b2a9190613978565b611b465760405162461bcd60e51b81526004016107f2906142b0565b611b536001840182611fb4565b506002546040516210343160e01b81526001600160a01b03909116906210343190611b82908490600401613f47565b600060405180830381600087803b158015611b9c57600080fd5b505af1158015611bb0573d6000803e3d6000fd5b505060019093019250611a7e915050565b507fb14273dec0b1f9ed3657f0f047101d6250bbbb76a6c3e7b808dcca46bec0db4c878787878787604051611bfb9695949392919061402b565b60405180910390a150505050505050565b600080516020614aa683398151915281565b60008181526020819052604081206112d990612d46565b600082815260208190526040902060020154611c53906105ac61223e565b610a545760405162461bcd60e51b8152600401808060200182810382526030815260200180614a556030913960400191505060405180910390fd5b6003546001600160a01b031681565b6004546001600160a01b031681565b611cb76000336112df565b611cd35760405162461bcd60e51b81526004016107f290614473565b6000818152600660205260409020611cea81612d51565b6003810154611d0f906001600160a01b03600160601b8204169063ffffffff16612d9d565b817f1870528c09977fe75036f8586518e5853addf6419747cbb81294106f734fe2e18260000183600301600c9054906101000a90046001600160a01b031633604051611d5d9392919061409d565b60405180910390a25050565b611d746000336112df565b611d905760405162461bcd60e51b81526004016107f290614473565b600180546001600160a01b03958616620100000262010000600160b01b0319909116179055600280549385166001600160a01b0319948516179055600380549285169284169290921790915560048054919093169116179055565b611df36137da565b600082815260066020908152604091829020825181546002610100600183161581026000190190921604601f810185900490940282016101209081019095528101838152909391928492849190840182828015611e915780601f10611e6657610100808354040283529160200191611e91565b820191906000526020600020905b815481529060010190602001808311611e7457829003601f168201915b50505050508152602001600182018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015611f335780601f10611f0857610100808354040283529160200191611f33565b820191906000526020600020905b815481529060010190602001808311611f1657829003601f168201915b50505091835250506002820154602082015260038083015463ffffffff8082166040850152600160201b820481166060850152600160401b8204166080840152600160601b90046001600160a01b031660a0830152600483015460c09092019160ff1690811115611fa057fe5b6003811115611fab57fe5b90525092915050565b60006112d6836001600160a01b038416612dcf565b60006112d6836001600160a01b038416612e19565b8154600090611ffd9086908690600160401b900463ffffffff1661148c565b6000818152600660205260408120919250600482015460ff16600381111561202157fe5b1415806120525750600381015463ffffffff600160201b9091048116610708011661204a6123cb565b63ffffffff16115b61206e5760405162461bcd60e51b81526004016107f29061465c565b6120788183612b34565b82801561209757506002600482015460ff16600381111561209557fe5b145b156120b75760048101805460ff19169055835463ffffffff60201b191684555b6000828152600660205260408120906120d08282613796565b6120de600183016000613796565b50600060028201819055600382018190556004909101805460ff191690555b61210985600101612d46565b81101561218f57600061211f6001870183612c8d565b6002546040516304c8eeb560e21b81529192506001600160a01b031690631323bad490612150908490600401613f47565b600060405180830381600087803b15801561216a57600080fd5b505af115801561217e573d6000803e3d6000fd5b5050600190930192506120fd915050565b508354600160201b900463ffffffff16156121bc5760405162461bcd60e51b81526004016107f29061451a565b600586866040516121ce929190613e63565b90815260405190819003602001902080546fffffffffffffffffffffffffffffffff19168155600060018201818181612207828261381c565b5050505050507feacdb5801d8160882398b78a544379ff28cbe8bd4de76aca39013c89311e00d58686604051611477929190613fe6565b3390565b600082815260208190526040902061225a9082611fb4565b156109e45761226761223e565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b60008281526020819052604090206122c39082612e31565b156109e4576122d061223e565b6001600160a01b0316816001600160a01b0316837ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b60405160405180910390a45050565b6003600482015460ff16600381111561232957fe5b146123465760405162461bcd60e51b81526004016107f290614761565b61234f81612e46565b50565b600061236661235f612e8b565b8390612e94565b600154604051630852cd8d60e31b81529192506201000090046001600160a01b0316906342966c689061239d908490600401613f9e565b600060405180830381600087803b1580156123b757600080fd5b505af1158015611249573d6000803e3d6000fd5b4290565b80820163ffffffff80841690821610156112d957600080fd5b6001600160a01b03821660008181526007602052604090819020805463ffffffff191663ffffffff8516179055517fc606d47187d0dea01a7ca1a0d8abf6027f8841bf0f82fab363549489e139e2ce90611d5d90849061489b565b61244b610fdd565b612493576040805162461bcd60e51b815260206004820152601460248201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b604482015290519081900360640190fd5b6001805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa6124c661223e565b604080516001600160a01b039092168252519081900360200190a1565b8854600160601b900463ffffffff168610156125115760405162461bcd60e51b81526004016107f29061449f565b600061025861251e6123cb565b019050600061259660405160200161253590613ef8565b604051602081830303815290604052805190602001208b600001358c602001358d60400160208101906125689190613ce4565b60405160200161257b9493929190613fa7565b60405160208183030381529060405280519060200120612eed565b905060005b888110156126f75760008a8a838181106125b157fe5b90506020020160208101906125c691906138e8565b90506125d06123cb565b6001600160a01b03821660009081526007602052604090205463ffffffff918216911611156126115760405162461bcd60e51b81526004016107f2906145f6565b61261e60018e0182611fc9565b61263a5760405162461bcd60e51b81526004016107f29061468c565b806001600160a01b0316600184878c8c8781811061265457fe5b905060200201358b8b8881811061266757fe5b905060200201356040516000815260200160405260405161268b9493929190613fc8565b6020604051602081039080840390855afa1580156126ad573d6000803e3d6000fd5b505050602060405103516001600160a01b0316146126dd5760405162461bcd60e51b81526004016107f290614155565b600885901c94506126ee81856123e8565b5060010161259b565b505050505050505050505050565b6001600484015460ff16600381111561271a57fe5b146127375760405162461bcd60e51b81526004016107f29061439a565b600283810183905560038401805463ffffffff60401b1916600160401b63ffffffff85160217905560048401805460ff19166001835b0217905550505050565b600061278461235f612e8b565b6004805460405163b41a593760e01b81529293506000926001600160a01b039091169163b41a5937916127bb918891879101613f5b565b602060405180830381600087803b1580156127d557600080fd5b505af11580156127e9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061280d9190613ccc565b905080156128fb57600154600480546040516340c10f1960e01b81526001600160a01b03620100009094048416936340c10f19936128519390911691869101613f5b565b600060405180830381600087803b15801561286b57600080fd5b505af115801561287f573d6000803e3d6000fd5b50506001546201000090046001600160a01b031691506340c10f199050856128a78585612f39565b6040518363ffffffff1660e01b81526004016128c4929190613f5b565b600060405180830381600087803b1580156128de57600080fd5b505af11580156128f2573d6000803e3d6000fd5b5050505061097b565b6001546040516340c10f1960e01b8152620100009091046001600160a01b0316906340c10f19906129329087908690600401613f5b565b600060405180830381600087803b15801561294c57600080fd5b505af1158015612960573d6000803e3d6000fd5b5050505050505050565b61297381612f96565b817f24da21178c24a520845b59d6d351ecf3033e3ceefb8c66f286f667caa74cd2c38260000133604051611d5d929190614073565b6002600484015460ff1660038111156129bd57fe5b146129da5760405162461bcd60e51b81526004016107f2906143e2565b6003830180546bffffffffffffffffffffffff1633600160601b02179055612a0660018401838361370a565b50612a0f6123cb565b6003808501805463ffffffff93909316600160201b0263ffffffff60201b199093169290921790915560048401805460ff191660018361276d565b6000612a5761235f612e8b565b60048054604051632daf8ec560e11b81529293506000926001600160a01b0390911691635b5f1d8a91612a8e918991879101613f5b565b602060405180830381600087803b158015612aa857600080fd5b505af1158015612abc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ae09190613ccc565b9050612b0b8585612af18585612fc8565b6001546201000090046001600160a01b0316929190613022565b801561110057600454600154611100916001600160a01b0362010000909204821691168361307c565b6003600483015460ff166003811115612b4957fe5b1415612ba257600382015463ffffffff600160201b90910481166170800116612b706123cb565b63ffffffff1611612b935760405162461bcd60e51b81526004016107f29061459e565b612b9d81836130ce565b6109e4565b6001600483015460ff166003811115612bb757fe5b14156109e457600382015463ffffffff600160201b909104811661fd200116612bde6123cb565b63ffffffff1611612c015760405162461bcd60e51b81526004016107f290614545565b6109e48183613175565b612c13610fdd565b15612c58576040805162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b604482015290519081900360640190fd5b6001805460ff1916811790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586124c661223e565b60006112d683836131b5565b6000600485015460ff166003811115612cae57fe5b14612ccb5760405162461bcd60e51b81526004016107f2906142f2565b612cd684848461370a565b506003840180546bffffffffffffffffffffffff1633600160601b021763ffffffff191663ffffffff8316179055612d0c6123cb565b60038501805463ffffffff92909216600160201b0263ffffffff60201b19909216919091179055505050600401805460ff19166001179055565b60006112d982613219565b6003600482015460ff166003811115612d6657fe5b14612d835760405162461bcd60e51b81526004016107f290614429565b6004810180546002919060ff19166001835b021790555050565b6000612daa61235f612e8b565b600154909150612dca906201000090046001600160a01b0316848361307c565b505050565b6000612ddb8383612e19565b612e11575081546001818101845560008481526020808220909301849055845484825282860190935260409020919091556112d9565b5060006112d9565b60009081526001919091016020526040902054151590565b60006112d6836001600160a01b03841661321d565b612e4e6123cb565b60038201805463ffffffff92909216600160201b0263ffffffff60201b199092169190911790556004810180546000919060ff1916600183612d95565b6402540be40090565b600082612ea3575060006112d9565b82820282848281612eb057fe5b04146112d65760405162461bcd60e51b8152600401808060200182810382526021815260200180614a856021913960400191505060405180910390fd5b6000612ef76132e3565b82604051602001808061190160f01b81525060020183815260200182815260200192505050604051602081830303815290604052805190602001209050919050565b600082821115612f90576040805162461bcd60e51b815260206004820152601e60248201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604482015290519081900360640190fd5b50900390565b6001600482015460ff166003811115612fab57fe5b146123465760405162461bcd60e51b81526004016107f290614333565b6000828201838110156112d6576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b604080516001600160a01b0380861660248301528416604482015260648082018490528251808303909101815260849091019091526020810180516001600160e01b03166323b872dd60e01b17905261097b9085906133ad565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b179052612dca9084906133ad565b60048101805460ff1916905560038101546130ee9063ffffffff16612352565b60006005826000016040516131039190613e88565b9081526040516020918190039190910181206003840154815463ffffffff60201b19811663ffffffff928316600160201b9283900484160390921602178155915083907fd90232357c1ad1e207410849ce0a0374bac4c22771453838f285ceca745547e390610c1f9085903390614073565b60048101805460ff1916905560405182907f24da21178c24a520845b59d6d351ecf3033e3ceefb8c66f286f667caa74cd2c390611d5d9084903390614073565b815460009082106131f75760405162461bcd60e51b81526004018080602001828103825260228152602001806149be6022913960400191505060405180910390fd5b82600001828154811061320657fe5b9060005260206000200154905092915050565b5490565b600081815260018301602052604081205480156132d9578354600019808301919081019060009087908390811061325057fe5b906000526020600020015490508087600001848154811061326d57fe5b60009182526020808320909101929092558281526001898101909252604090209084019055865487908061329d57fe5b600190038181906000526020600020016000905590558660010160008781526020019081526020016000206000905560019450505050506112d9565b60009150506112d9565b60007f000000000000000000000000000000000000000000000000000000000000000061330e61345e565b141561333b57507f0000000000000000000000000000000000000000000000000000000000000000610cef565b6133a67f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000613462565b9050610cef565b6000613402826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166134c49092919063ffffffff16565b805190915015612dca5780806020019051602081101561342157600080fd5b5051612dca5760405162461bcd60e51b815260040180806020018281038252602a815260200180614ac6602a913960400191505060405180910390fd5b4690565b600083838361346f61345e565b3060405160200180868152602001858152602001848152602001838152602001826001600160a01b03168152602001955050505050506040516020818303038152906040528051906020012090509392505050565b60606134d384846000856134db565b949350505050565b60608247101561351c5760405162461bcd60e51b8152600401808060200182810382526026815260200180614a2f6026913960400191505060405180910390fd5b61352585613636565b613576576040805162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015290519081900360640190fd5b600080866001600160a01b031685876040518082805190602001908083835b602083106135b45780518252601f199092019160209182019101613595565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d8060008114613616576040519150601f19603f3d011682016040523d82523d6000602084013e61361b565b606091505b509150915061362b82828661363c565b979650505050505050565b3b151590565b6060831561364b5750816114bc565b82511561365b5782518084602001fd5b8160405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b838110156136a557818101518382015260200161368d565b50505050905090810190601f1680156136d25780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b60405180606001604052806000801916815260200160608152602001600063ffffffff1681525090565b828054600181600116156101000203166002900490600052602060002090601f0160209004810192826137405760008555613786565b82601f106137595782800160ff19823516178555613786565b82800160010185558215613786579182015b8281111561378657823582559160200191906001019061376b565b50613792929150613836565b5090565b50805460018160011615610100020316600290046000825580601f106137bc575061234f565b601f01602090049060005260206000209081019061234f9190613836565b6040805161010081018252606080825260208201819052600092820183905281018290526080810182905260a0810182905260c081018290529060e082015290565b508054600082559060005260206000209081019061234f91905b5b808211156137925760008155600101613837565b60008083601f84011261385c578182fd5b50813567ffffffffffffffff811115613873578182fd5b602083019150836020808302850101111561388d57600080fd5b9250929050565b60008083601f8401126138a5578182fd5b50813567ffffffffffffffff8111156138bc578182fd5b60208301915083602082850101111561388d57600080fd5b803563ffffffff8116811461086a57600080fd5b6000602082840312156138f9578081fd5b81356112d6816149a8565b60008060408385031215613916578081fd5b8235613921816149a8565b915061392f602084016138d4565b90509250929050565b6000806020838503121561394a578182fd5b823567ffffffffffffffff811115613960578283fd5b61396c8582860161384b565b90969095509350505050565b600060208284031215613989578081fd5b815180151581146112d6578182fd5b6000602082840312156139a9578081fd5b5035919050565b600080604083850312156139c2578182fd5b8235915060208301356139d4816149a8565b809150509250929050565b6000806000604084860312156139f3578081fd5b83359250602084013567ffffffffffffffff811115613a10578182fd5b613a1c86828701613894565b9497909650939450505050565b60008060408385031215613a3b578182fd5b50508035926020909101359150565b60008060008060808587031215613a5f578081fd5b8435613a6a816149a8565b93506020850135613a7a816149a8565b92506040850135613a8a816149a8565b91506060850135613a9a816149a8565b939692955090935050565b60008060208385031215613ab7578182fd5b823567ffffffffffffffff811115613acd578283fd5b61396c85828601613894565b600080600060408486031215613aed578081fd5b833567ffffffffffffffff811115613b03578182fd5b613b0f86828701613894565b909790965060209590950135949350505050565b60008060008060608587031215613b38578182fd5b843567ffffffffffffffff811115613b4e578283fd5b613b5a87828801613894565b9095509350613b6d9050602086016138d4565b9150613b7b604086016138d4565b905092959194509250565b60008060008060008060808789031215613b9e578384fd5b863567ffffffffffffffff80821115613bb5578586fd5b613bc18a838b01613894565b9098509650869150613bd560208a016138d4565b9550613be360408a016138d4565b94506060890135915080821115613bf8578384fd5b50613c0589828a0161384b565b979a9699509497509295939492505050565b600080600080600080600080888a0360e0811215613c33578687fd5b6060811215613c40578687fd5b50889750606089013567ffffffffffffffff80821115613c5e578788fd5b613c6a8c838d0161384b565b909950975060808b0135915080821115613c82578384fd5b613c8e8c838d0161384b565b909750955060a08b0135915080821115613ca6578384fd5b50613cb38b828c0161384b565b999c989b50969995989497949560c00135949350505050565b600060208284031215613cdd578081fd5b5051919050565b600060208284031215613cf5578081fd5b6112d6826138d4565b6001600160a01b03169052565b60008284526020808501945082825b85811015613d48578135613d2d816149a8565b6001600160a01b031687529582019590820190600101613d1a565b509495945050505050565b60048110613d5d57fe5b9052565b60008284528282602086013780602084860101526020601f19601f85011685010190509392505050565b60008151808452815b81811015613db057602081850181015186830182015201613d94565b81811115613dc15782602083870101525b50601f01601f19169290920160200192915050565b60008154600180821660008114613df45760018114613e1257613e50565b60028304607f16865260ff1983166020870152604086019350613e50565b60028304808752613e228661499c565b60005b82811015613e465781546020828b0101528482019150602081019050613e25565b8801602001955050505b50505092915050565b63ffffffff169052565b6000828483379101908152919050565b60008385833750909101908152602001919050565b6000808354600180821660008114613ea75760018114613ebe57613eed565b60ff198316865260028304607f1686019350613eed565b600283048786526020808720875b83811015613ee55781548a820152908501908201613ecc565b505050860193505b509195945050505050565b7f4d696e74526571756573742862797465733332207265636569707449642c627981527f746573333220747849642c75696e7432353620686569676874290000000000006020820152603a0190565b6001600160a01b0391909116815260200190565b6001600160a01b03929092168252602082015260400190565b6001600160a01b0392909216825263ffffffff16602082015260400190565b901515815260200190565b90815260200190565b9384526020840192909252604083015263ffffffff16606082015260800190565b93845260ff9290921660208401526040830152606082015260800190565b6000602082526134d3602083018486613d61565b60006060825261400e606083018688613d61565b905083602083015263ffffffff8316604083015295945050505050565b60006080825261403f60808301888a613d61565b63ffffffff8781166020850152861660408401528281036060840152614066818587613d0b565b9998505050505050505050565b6000604082526140866040830185613dd6565b905060018060a01b03831660208301529392505050565b6000606082526140b06060830186613dd6565b6001600160a01b0394851660208401529290931660409091015292915050565b6000608082526140e36080830188613dd6565b82810360208401526140f6818789613d0b565b91505083604083015263ffffffff831660608301529695505050505050565b6000606082526141286060830187613dd6565b828103602084015261413b818688613d61565b91505060018060a01b038316604083015295945050505050565b602080825260119082015270696e76616c6964207369676e617475726560781b604082015260600190565b60208082526012908201527133b937bab81031b7b7b634b733903237bbb760711b604082015260600190565b60208082526018908201527f726571756972652067726f75702061646d696e20726f6c650000000000000000604082015260600190565b6020808252601e908201527f72656365697074206e6f7420696e20617661696c61626c652073746174650000604082015260600190565b6020808252601b908201527f616d6f756e7420657863656564206d617820616c6c6f77616e63650000000000604082015260600190565b6020808252600e908201526d1bdb9b1e481c9958da5c1a595b9d60921b604082015260600190565b6020808252601f908201527f73686f756c642066696c6c20616c6c2067726f757020616c6c6f77616e636500604082015260600190565b60208082526022908201527f6b65657065722068617320696e73756666696369656e7420636f6c6c61746572604082015261185b60f21b606082015260800190565b60208082526021908201527f72656365697074206973206e6f7420696e20417661696c61626c6520737461746040820152606560f81b606082015260800190565b6020808252601f908201527f72656365697074206973206e6f74204465706f73697452657175657374656400604082015260600190565b60208082526016908201527519dc9bdd5c081a5908185b1c9958591e48195e1a5cdd60521b604082015260600190565b60208082526028908201527f72656365697074206973206e6f7420696e204465706f73697452657175657374604082015267656420737461746560c01b606082015260800190565b60208082526027908201527f72656365697074206973206e6f7420696e204465706f736974526563656976656040820152666420737461746560c81b606082015260800190565b6020808252602a908201527f72656365697074206973206e6f7420696e2057697468647261775265717565736040820152697465642073746174757360b01b606082015260800190565b602080825260129082015271726571756972652061646d696e20726f6c6560701b604082015260600190565b6020808252601290820152716e6f7420656e6f756768206b65657065727360701b604082015260600190565b6020808252600d908201526c696e76616c6964206e6f6e636560981b604082015260600190565b6020808252600e908201526d1b9bdd08185d5d1a1bdc9a5e995960921b604082015260600190565b602080825260119082015270067726f75702062616c616e6365203e203607c1b604082015260600190565b6020808252601390820152726465706f73697420696e2070726f677265737360681b604082015260600190565b6020808252601290820152717265717569726520677561726420726f6c6560701b604082015260600190565b602080825260149082015273776974686472617720696e2070726f677265737360601b604082015260600190565b60208082526010908201526f3932b33ab7321031b7b7b6103237bbb760811b604082015260600190565b60208082526015908201527435b2b2b832b91034b99034b71031b7b7b63237bbb760591b604082015260600190565b6020808252601b908201527f776f726b696e67207265636569707420696e2070726f67726573730000000000604082015260600190565b60208082526016908201527507265636569707420696e2072657375696e67206761760541b604082015260600190565b60208082526016908201527506b6565706572206973206e6f7420696e2067726f75760541b604082015260600190565b60208082526019908201527f72657175697265207265636569707420726563697069656e7400000000000000604082015260600190565b60208082526017908201527f6b65657065722065786974206e6f7420616c6c6f776564000000000000000000604082015260600190565b60208082526017908201527f616d6f756e742030206973206e6f7420616c6c6f776564000000000000000000604082015260600190565b6020808252602b908201527f72656365697074206973206e6f7420696e20776974686472617720726571756560408201526a737465642073746174757360a81b606082015260800190565b600060208252825160208301526020830151606060408401526147d26080840182613d8b565b905063ffffffff60408501511660608401528091505092915050565b600060208252825161010080602085015261480d610120850183613d8b565b91506020850151601f1985840301604086015261482a8382613d8b565b9250506040850151606085015263ffffffff6060860151166080850152608085015161485960a0860182613e59565b5060a085015161486c60c0860182613e59565b5060c085015161487f60e0860182613cfe565b5060e085015161489182860182613d53565b5090949350505050565b63ffffffff91909116815260200190565b600063ffffffff85168252604060208301526148cc604083018486613d61565b95945050505050565b600060c0820163ffffffff808a1684526020818a16818601528189166040860152818816606086015260c06080860152829150865180845260e0860192508188019350845b8181101561493f5784516001600160a01b03168452938201939282019260010161491a565b50505060a09390930193909352509695505050505050565b6000808335601e1984360301811261496d578283fd5b83018035915067ffffffffffffffff821115614987578283fd5b60200191503681900382131561388d57600080fd5b60009081526020902090565b6001600160a01b038116811461234f57600080fdfe456e756d657261626c655365743a20696e646578206f7574206f6620626f756e6473416363657373436f6e74726f6c3a2073656e646572206d75737420626520616e2061646d696e20746f206772616e74043c983c49d46f0e102151eaf8085d4a2e6571d5df2d47b013f39bddfd4a639d416464726573733a20696e73756666696369656e742062616c616e636520666f722063616c6c416363657373436f6e74726f6c3a2073656e646572206d75737420626520616e2061646d696e20746f207265766f6b65536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f778d6b526d6b13eab5a67762424267752264b236dd46b48a705b5b5249049123f75361666545524332303a204552433230206f7065726174696f6e20646964206e6f742073756363656564416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636520726f6c657320666f722073656c66a264697066735822122042b4f78d92268d419043afe999bf0a51bfaa4b931d5128a1fed8cdb2aad6d37d64736f6c63430007060033

Block Transaction Gas Used Reward
Age Block Fee Address BC Fee Address Voting Power Jailed Incoming
Block Uncle Number Difficulty Gas Used Reward
Loading
Loading
Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for interesting conversations.