ETH Price: $2,131.35 (-11.35%)

Transaction Decoder

Block:
20118154 at Jun-18-2024 10:50:11 AM +UTC
Transaction Fee:
0.000906409790764312 ETH $1.93
Gas Used:
163,574 Gas / 5.541282788 Gwei

Emitted Events:

173 VOWToken.Sent( operator=[Receiver] DBridge, from=[Sender] 0x5396e00615214cb50c911164adb931ae505e46f4, to=[Receiver] DBridge, amount=2900000000000000000000, data=0x, operatorData=0x )
174 VOWToken.Transfer( from=[Sender] 0x5396e00615214cb50c911164adb931ae505e46f4, to=[Receiver] DBridge, value=2900000000000000000000 )
175 DBridge.Deposit( bridgeId=F01F98BBC5C28FABD153A3915970FD313CC991E5A71FF16A0A29BB9F1BBBDEB6, bridgeIndex=5385, sender=[Sender] 0x5396e00615214cb50c911164adb931ae505e46f4, chainId=56, tokenAddress=0xF585B5b4...62630397b, amount=2900000000000000000000 )

Account State Difference:

  Address   Before After State Difference Code
0x1BBf25e7...F4bE946Fb
2.241340172419162793 Eth2.241503746419162793 Eth0.000163574
0x5396E006...E505e46F4
0.035231585548349398 Eth
Nonce: 1266
0.034325175757585086 Eth
Nonce: 1267
0.000906409790764312
0xa7C14010...57dde644d

Execution Trace

DBridge.deposit( chainId=56, tokenAddress=0x1BBf25e71EC48B84d773809B4bA55B6F4bE946Fb, amount=2900000000000000000000 ) => ( bridgeId=F01F98BBC5C28FABD153A3915970FD313CC991E5A71FF16A0A29BB9F1BBBDEB6 )
  • VOWToken.transferFrom( _from=0x5396E00615214Cb50C911164AdB931AE505e46F4, _to=0xa7C14010afA616fa23A2Bb0A94d76Dd57dde644d, _value=2900000000000000000000 ) => ( success_=True )
    • LToken.2e6a5609( )
      • ERC1820Registry.getInterfaceImplementer( _addr=0x5396E00615214Cb50C911164AdB931AE505e46F4, _interfaceHash=29DDB589B1FB5FC7CF394961C1ADF5F8C6454761ADF795E67FE149F658ABE895 ) => ( 0x0000000000000000000000000000000000000000 )
      • ERC1820Registry.getInterfaceImplementer( _addr=0xa7C14010afA616fa23A2Bb0A94d76Dd57dde644d, _interfaceHash=B281FC8C12954D22544DB45DE3159A39272895B169A852B314F9CC762E44C53B ) => ( 0x0000000000000000000000000000000000000000 )
        File 1 of 4: DBridge
        // SPDX-License-Identifier: MIT
        // OpenZeppelin Contracts (last updated v5.0.0) (access/AccessControl.sol)
        pragma solidity ^0.8.20;
        import {IAccessControl} from "./IAccessControl.sol";
        import {Context} from "../utils/Context.sol";
        import {ERC165} from "../utils/introspection/ERC165.sol";
        /**
         * @dev Contract module that allows children to implement role-based access
         * control mechanisms. This is a lightweight version that doesn't allow enumerating role
         * members except through off-chain means by accessing the contract event logs. Some
         * applications may benefit from on-chain enumerability, for those cases see
         * {AccessControlEnumerable}.
         *
         * Roles are referred to by their `bytes32` identifier. These should be exposed
         * in the external API and be unique. The best way to achieve this is by
         * using `public constant` hash digests:
         *
         * ```solidity
         * 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}:
         *
         * ```solidity
         * 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. We recommend using {AccessControlDefaultAdminRules}
         * to enforce additional security measures for this role.
         */
        abstract contract AccessControl is Context, IAccessControl, ERC165 {
            struct RoleData {
                mapping(address account => bool) hasRole;
                bytes32 adminRole;
            }
            mapping(bytes32 role => RoleData) private _roles;
            bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
            /**
             * @dev Modifier that checks that an account has a specific role. Reverts
             * with an {AccessControlUnauthorizedAccount} error including the required role.
             */
            modifier onlyRole(bytes32 role) {
                _checkRole(role);
                _;
            }
            /**
             * @dev See {IERC165-supportsInterface}.
             */
            function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
                return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
            }
            /**
             * @dev Returns `true` if `account` has been granted `role`.
             */
            function hasRole(bytes32 role, address account) public view virtual returns (bool) {
                return _roles[role].hasRole[account];
            }
            /**
             * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()`
             * is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier.
             */
            function _checkRole(bytes32 role) internal view virtual {
                _checkRole(role, _msgSender());
            }
            /**
             * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`
             * is missing `role`.
             */
            function _checkRole(bytes32 role, address account) internal view virtual {
                if (!hasRole(role, account)) {
                    revert AccessControlUnauthorizedAccount(account, role);
                }
            }
            /**
             * @dev Returns the admin role that controls `role`. See {grantRole} and
             * {revokeRole}.
             *
             * To change a role's admin, use {_setRoleAdmin}.
             */
            function getRoleAdmin(bytes32 role) public view virtual returns (bytes32) {
                return _roles[role].adminRole;
            }
            /**
             * @dev Grants `role` to `account`.
             *
             * If `account` had not been already granted `role`, emits a {RoleGranted}
             * event.
             *
             * Requirements:
             *
             * - the caller must have ``role``'s admin role.
             *
             * May emit a {RoleGranted} event.
             */
            function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
                _grantRole(role, account);
            }
            /**
             * @dev Revokes `role` from `account`.
             *
             * If `account` had been granted `role`, emits a {RoleRevoked} event.
             *
             * Requirements:
             *
             * - the caller must have ``role``'s admin role.
             *
             * May emit a {RoleRevoked} event.
             */
            function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
                _revokeRole(role, account);
            }
            /**
             * @dev Revokes `role` from the calling account.
             *
             * Roles are often managed via {grantRole} and {revokeRole}: this function's
             * purpose is to provide a mechanism for accounts to lose their privileges
             * if they are compromised (such as when a trusted device is misplaced).
             *
             * If the calling account had been revoked `role`, emits a {RoleRevoked}
             * event.
             *
             * Requirements:
             *
             * - the caller must be `callerConfirmation`.
             *
             * May emit a {RoleRevoked} event.
             */
            function renounceRole(bytes32 role, address callerConfirmation) public virtual {
                if (callerConfirmation != _msgSender()) {
                    revert AccessControlBadConfirmation();
                }
                _revokeRole(role, callerConfirmation);
            }
            /**
             * @dev Sets `adminRole` as ``role``'s admin role.
             *
             * Emits a {RoleAdminChanged} event.
             */
            function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
                bytes32 previousAdminRole = getRoleAdmin(role);
                _roles[role].adminRole = adminRole;
                emit RoleAdminChanged(role, previousAdminRole, adminRole);
            }
            /**
             * @dev Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.
             *
             * Internal function without access restriction.
             *
             * May emit a {RoleGranted} event.
             */
            function _grantRole(bytes32 role, address account) internal virtual returns (bool) {
                if (!hasRole(role, account)) {
                    _roles[role].hasRole[account] = true;
                    emit RoleGranted(role, account, _msgSender());
                    return true;
                } else {
                    return false;
                }
            }
            /**
             * @dev Attempts to revoke `role` to `account` and returns a boolean indicating if `role` was revoked.
             *
             * Internal function without access restriction.
             *
             * May emit a {RoleRevoked} event.
             */
            function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {
                if (hasRole(role, account)) {
                    _roles[role].hasRole[account] = false;
                    emit RoleRevoked(role, account, _msgSender());
                    return true;
                } else {
                    return false;
                }
            }
        }
        // SPDX-License-Identifier: MIT
        // OpenZeppelin Contracts (last updated v5.0.0) (access/IAccessControl.sol)
        pragma solidity ^0.8.20;
        /**
         * @dev External interface of AccessControl declared to support ERC165 detection.
         */
        interface IAccessControl {
            /**
             * @dev The `account` is missing a role.
             */
            error AccessControlUnauthorizedAccount(address account, bytes32 neededRole);
            /**
             * @dev The caller of a function is not the expected one.
             *
             * NOTE: Don't confuse with {AccessControlUnauthorizedAccount}.
             */
            error AccessControlBadConfirmation();
            /**
             * @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.
             */
            event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
            /**
             * @dev Emitted when `account` is granted `role`.
             *
             * `sender` is the account that originated the contract call, an admin role
             * bearer except when using {AccessControl-_setupRole}.
             */
            event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
            /**
             * @dev Emitted when `account` is revoked `role`.
             *
             * `sender` is the account that originated the contract call:
             *   - if using `revokeRole`, it is the admin role bearer
             *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
             */
            event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
            /**
             * @dev Returns `true` if `account` has been granted `role`.
             */
            function hasRole(bytes32 role, address account) external view returns (bool);
            /**
             * @dev Returns the admin role that controls `role`. See {grantRole} and
             * {revokeRole}.
             *
             * To change a role's admin, use {AccessControl-_setRoleAdmin}.
             */
            function getRoleAdmin(bytes32 role) external view returns (bytes32);
            /**
             * @dev Grants `role` to `account`.
             *
             * If `account` had not been already granted `role`, emits a {RoleGranted}
             * event.
             *
             * Requirements:
             *
             * - the caller must have ``role``'s admin role.
             */
            function grantRole(bytes32 role, address account) external;
            /**
             * @dev Revokes `role` from `account`.
             *
             * If `account` had been granted `role`, emits a {RoleRevoked} event.
             *
             * Requirements:
             *
             * - the caller must have ``role``'s admin role.
             */
            function revokeRole(bytes32 role, address account) external;
            /**
             * @dev Revokes `role` from the calling account.
             *
             * Roles are often managed via {grantRole} and {revokeRole}: this function's
             * purpose is to provide a mechanism for accounts to lose their privileges
             * if they are compromised (such as when a trusted device is misplaced).
             *
             * If the calling account had been granted `role`, emits a {RoleRevoked}
             * event.
             *
             * Requirements:
             *
             * - the caller must be `callerConfirmation`.
             */
            function renounceRole(bytes32 role, address callerConfirmation) external;
        }
        // SPDX-License-Identifier: MIT
        // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)
        pragma solidity ^0.8.20;
        /**
         * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
         * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
         *
         * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
         * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
         * need to send a transaction, and thus is not required to hold Ether at all.
         *
         * ==== Security Considerations
         *
         * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
         * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
         * considered as an intention to spend the allowance in any specific way. The second is that because permits have
         * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
         * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
         * generally recommended is:
         *
         * ```solidity
         * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
         *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
         *     doThing(..., value);
         * }
         *
         * function doThing(..., uint256 value) public {
         *     token.safeTransferFrom(msg.sender, address(this), value);
         *     ...
         * }
         * ```
         *
         * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
         * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
         * {SafeERC20-safeTransferFrom}).
         *
         * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
         * contracts should have entry points that don't rely on permit.
         */
        interface IERC20Permit {
            /**
             * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
             * given ``owner``'s signed approval.
             *
             * IMPORTANT: The same issues {IERC20-approve} has related to transaction
             * ordering also apply here.
             *
             * Emits an {Approval} event.
             *
             * Requirements:
             *
             * - `spender` cannot be the zero address.
             * - `deadline` must be a timestamp in the future.
             * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
             * over the EIP712-formatted function arguments.
             * - the signature must use ``owner``'s current nonce (see {nonces}).
             *
             * For more information on the signature format, see the
             * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
             * section].
             *
             * CAUTION: See Security Considerations above.
             */
            function permit(
                address owner,
                address spender,
                uint256 value,
                uint256 deadline,
                uint8 v,
                bytes32 r,
                bytes32 s
            ) external;
            /**
             * @dev Returns the current nonce for `owner`. This value must be
             * included whenever a signature is generated for {permit}.
             *
             * Every successful call to {permit} increases ``owner``'s nonce by one. This
             * prevents a signature from being used multiple times.
             */
            function nonces(address owner) external view returns (uint256);
            /**
             * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
             */
            // solhint-disable-next-line func-name-mixedcase
            function DOMAIN_SEPARATOR() external view returns (bytes32);
        }
        // SPDX-License-Identifier: MIT
        // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)
        pragma solidity ^0.8.20;
        /**
         * @dev Interface of the ERC20 standard as defined in the EIP.
         */
        interface IERC20 {
            /**
             * @dev Emitted when `value` tokens are moved from one account (`from`) to
             * another (`to`).
             *
             * Note that `value` may be zero.
             */
            event Transfer(address indexed from, address indexed to, uint256 value);
            /**
             * @dev Emitted when the allowance of a `spender` for an `owner` is set by
             * a call to {approve}. `value` is the new allowance.
             */
            event Approval(address indexed owner, address indexed spender, uint256 value);
            /**
             * @dev Returns the value of tokens in existence.
             */
            function totalSupply() external view returns (uint256);
            /**
             * @dev Returns the value of tokens owned by `account`.
             */
            function balanceOf(address account) external view returns (uint256);
            /**
             * @dev Moves a `value` amount of tokens from the caller's account to `to`.
             *
             * Returns a boolean value indicating whether the operation succeeded.
             *
             * Emits a {Transfer} event.
             */
            function transfer(address to, uint256 value) external returns (bool);
            /**
             * @dev Returns the remaining number of tokens that `spender` will be
             * allowed to spend on behalf of `owner` through {transferFrom}. This is
             * zero by default.
             *
             * This value changes when {approve} or {transferFrom} are called.
             */
            function allowance(address owner, address spender) external view returns (uint256);
            /**
             * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
             * caller's tokens.
             *
             * Returns a boolean value indicating whether the operation succeeded.
             *
             * IMPORTANT: Beware that changing an allowance with this method brings the risk
             * that someone may use both the old and the new allowance by unfortunate
             * transaction ordering. One possible solution to mitigate this race
             * condition is to first reduce the spender's allowance to 0 and set the
             * desired value afterwards:
             * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
             *
             * Emits an {Approval} event.
             */
            function approve(address spender, uint256 value) external returns (bool);
            /**
             * @dev Moves a `value` amount of tokens from `from` to `to` using the
             * allowance mechanism. `value` is then deducted from the caller's
             * allowance.
             *
             * Returns a boolean value indicating whether the operation succeeded.
             *
             * Emits a {Transfer} event.
             */
            function transferFrom(address from, address to, uint256 value) external returns (bool);
        }
        // SPDX-License-Identifier: MIT
        // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)
        pragma solidity ^0.8.20;
        import {IERC20} from "../IERC20.sol";
        import {IERC20Permit} from "../extensions/IERC20Permit.sol";
        import {Address} from "../../../utils/Address.sol";
        /**
         * @title SafeERC20
         * @dev Wrappers around ERC20 operations that throw on failure (when the token
         * contract returns false). Tokens that return no value (and instead revert or
         * throw on failure) are also supported, non-reverting calls are assumed to be
         * successful.
         * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
         * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
         */
        library SafeERC20 {
            using Address for address;
            /**
             * @dev An operation with an ERC20 token failed.
             */
            error SafeERC20FailedOperation(address token);
            /**
             * @dev Indicates a failed `decreaseAllowance` request.
             */
            error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
            /**
             * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
             * non-reverting calls are assumed to be successful.
             */
            function safeTransfer(IERC20 token, address to, uint256 value) internal {
                _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
            }
            /**
             * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
             * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
             */
            function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
                _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
            }
            /**
             * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
             * non-reverting calls are assumed to be successful.
             */
            function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
                uint256 oldAllowance = token.allowance(address(this), spender);
                forceApprove(token, spender, oldAllowance + value);
            }
            /**
             * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
             * value, non-reverting calls are assumed to be successful.
             */
            function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
                unchecked {
                    uint256 currentAllowance = token.allowance(address(this), spender);
                    if (currentAllowance < requestedDecrease) {
                        revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
                    }
                    forceApprove(token, spender, currentAllowance - requestedDecrease);
                }
            }
            /**
             * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
             * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
             * to be set to zero before setting it to a non-zero value, such as USDT.
             */
            function forceApprove(IERC20 token, address spender, uint256 value) internal {
                bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
                if (!_callOptionalReturnBool(token, approvalCall)) {
                    _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
                    _callOptionalReturn(token, approvalCall);
                }
            }
            /**
             * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
             * on the return value: the return value is optional (but if data is returned, it must not be false).
             * @param token The token targeted by the call.
             * @param data The call data (encoded using abi.encode or one of its variants).
             */
            function _callOptionalReturn(IERC20 token, bytes memory data) private {
                // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
                // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
                // the target address contains contract code and also asserts for success in the low-level call.
                bytes memory returndata = address(token).functionCall(data);
                if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
                    revert SafeERC20FailedOperation(address(token));
                }
            }
            /**
             * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
             * on the return value: the return value is optional (but if data is returned, it must not be false).
             * @param token The token targeted by the call.
             * @param data The call data (encoded using abi.encode or one of its variants).
             *
             * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
             */
            function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
                // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
                // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
                // and not revert is the subcall reverts.
                (bool success, bytes memory returndata) = address(token).call(data);
                return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
            }
        }
        // SPDX-License-Identifier: MIT
        // OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)
        pragma solidity ^0.8.20;
        /**
         * @dev Collection of functions related to the address type
         */
        library Address {
            /**
             * @dev The ETH balance of the account is not enough to perform the operation.
             */
            error AddressInsufficientBalance(address account);
            /**
             * @dev There's no code at `target` (it is not a contract).
             */
            error AddressEmptyCode(address target);
            /**
             * @dev A call to an address target failed. The target may have reverted.
             */
            error FailedInnerCall();
            /**
             * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
             * `recipient`, forwarding all available gas and reverting on errors.
             *
             * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
             * of certain opcodes, possibly making contracts go over the 2300 gas limit
             * imposed by `transfer`, making them unable to receive funds via
             * `transfer`. {sendValue} removes this limitation.
             *
             * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
             *
             * IMPORTANT: because control is transferred to `recipient`, care must be
             * taken to not create reentrancy vulnerabilities. Consider using
             * {ReentrancyGuard} or the
             * https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
             */
            function sendValue(address payable recipient, uint256 amount) internal {
                if (address(this).balance < amount) {
                    revert AddressInsufficientBalance(address(this));
                }
                (bool success, ) = recipient.call{value: amount}("");
                if (!success) {
                    revert FailedInnerCall();
                }
            }
            /**
             * @dev Performs a Solidity function call using a low level `call`. A
             * plain `call` is an unsafe replacement for a function call: use this
             * function instead.
             *
             * If `target` reverts with a revert reason or custom error, it is bubbled
             * up by this function (like regular Solidity function calls). However, if
             * the call reverted with no returned reason, this function reverts with a
             * {FailedInnerCall} error.
             *
             * Returns the raw returned data. To convert to the expected return value,
             * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
             *
             * Requirements:
             *
             * - `target` must be a contract.
             * - calling `target` with `data` must not revert.
             */
            function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                return functionCallWithValue(target, data, 0);
            }
            /**
             * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
             * but also transferring `value` wei to `target`.
             *
             * Requirements:
             *
             * - the calling contract must have an ETH balance of at least `value`.
             * - the called Solidity function must be `payable`.
             */
            function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
                if (address(this).balance < value) {
                    revert AddressInsufficientBalance(address(this));
                }
                (bool success, bytes memory returndata) = target.call{value: value}(data);
                return verifyCallResultFromTarget(target, success, returndata);
            }
            /**
             * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
             * but performing a static call.
             */
            function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                (bool success, bytes memory returndata) = target.staticcall(data);
                return verifyCallResultFromTarget(target, success, returndata);
            }
            /**
             * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
             * but performing a delegate call.
             */
            function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                (bool success, bytes memory returndata) = target.delegatecall(data);
                return verifyCallResultFromTarget(target, success, returndata);
            }
            /**
             * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
             * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
             * unsuccessful call.
             */
            function verifyCallResultFromTarget(
                address target,
                bool success,
                bytes memory returndata
            ) internal view returns (bytes memory) {
                if (!success) {
                    _revert(returndata);
                } else {
                    // only check if target is a contract if the call was successful and the return data is empty
                    // otherwise we already know that it was a contract
                    if (returndata.length == 0 && target.code.length == 0) {
                        revert AddressEmptyCode(target);
                    }
                    return returndata;
                }
            }
            /**
             * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
             * revert reason or with a default {FailedInnerCall} error.
             */
            function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
                if (!success) {
                    _revert(returndata);
                } else {
                    return returndata;
                }
            }
            /**
             * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
             */
            function _revert(bytes memory returndata) private pure {
                // Look for revert reason and bubble it up if present
                if (returndata.length > 0) {
                    // The easiest way to bubble the revert reason is using memory via assembly
                    /// @solidity memory-safe-assembly
                    assembly {
                        let returndata_size := mload(returndata)
                        revert(add(32, returndata), returndata_size)
                    }
                } else {
                    revert FailedInnerCall();
                }
            }
        }
        // SPDX-License-Identifier: MIT
        // OpenZeppelin Contracts (last updated v5.0.0) (utils/Context.sol)
        pragma solidity ^0.8.20;
        /**
         * @dev Provides information about the current execution context, including the
         * sender of the transaction and its data. While these are generally available
         * via msg.sender and msg.data, they should not be accessed in such a direct
         * manner, since when dealing with meta-transactions the account sending and
         * paying for execution may not be the actual sender (as far as an application
         * is concerned).
         *
         * This contract is only required for intermediate, library-like contracts.
         */
        abstract contract Context {
            function _msgSender() internal view virtual returns (address) {
                return msg.sender;
            }
            function _msgData() internal view virtual returns (bytes calldata) {
                return msg.data;
            }
        }
        // SPDX-License-Identifier: MIT
        // OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/ECDSA.sol)
        pragma solidity ^0.8.20;
        /**
         * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
         *
         * These functions can be used to verify that a message was signed by the holder
         * of the private keys of a given address.
         */
        library ECDSA {
            enum RecoverError {
                NoError,
                InvalidSignature,
                InvalidSignatureLength,
                InvalidSignatureS
            }
            /**
             * @dev The signature derives the `address(0)`.
             */
            error ECDSAInvalidSignature();
            /**
             * @dev The signature has an invalid length.
             */
            error ECDSAInvalidSignatureLength(uint256 length);
            /**
             * @dev The signature has an S value that is in the upper half order.
             */
            error ECDSAInvalidSignatureS(bytes32 s);
            /**
             * @dev Returns the address that signed a hashed message (`hash`) with `signature` or an error. This will not
             * return address(0) without also returning an error description. Errors are documented using an enum (error type)
             * and a bytes32 providing additional information about the error.
             *
             * If no error is returned, then the address can be used for verification purposes.
             *
             * The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
             * this function rejects them by requiring the `s` value to be in the lower
             * half order, and the `v` value to be either 27 or 28.
             *
             * IMPORTANT: `hash` _must_ be the result of a hash operation for the
             * verification to be secure: it is possible to craft signatures that
             * recover to arbitrary addresses for non-hashed data. A safe way to ensure
             * this is by receiving a hash of the original message (which may otherwise
             * be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
             *
             * Documentation for signature generation:
             * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
             * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
             */
            function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError, bytes32) {
                if (signature.length == 65) {
                    bytes32 r;
                    bytes32 s;
                    uint8 v;
                    // ecrecover takes the signature parameters, and the only way to get them
                    // currently is to use assembly.
                    /// @solidity memory-safe-assembly
                    assembly {
                        r := mload(add(signature, 0x20))
                        s := mload(add(signature, 0x40))
                        v := byte(0, mload(add(signature, 0x60)))
                    }
                    return tryRecover(hash, v, r, s);
                } else {
                    return (address(0), RecoverError.InvalidSignatureLength, bytes32(signature.length));
                }
            }
            /**
             * @dev Returns the address that signed a hashed message (`hash`) with
             * `signature`. This address can then be used for verification purposes.
             *
             * The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
             * this function rejects them by requiring the `s` value to be in the lower
             * half order, and the `v` value to be either 27 or 28.
             *
             * IMPORTANT: `hash` _must_ be the result of a hash operation for the
             * verification to be secure: it is possible to craft signatures that
             * recover to arbitrary addresses for non-hashed data. A safe way to ensure
             * this is by receiving a hash of the original message (which may otherwise
             * be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
             */
            function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
                (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, signature);
                _throwError(error, errorArg);
                return recovered;
            }
            /**
             * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
             *
             * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
             */
            function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError, bytes32) {
                unchecked {
                    bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
                    // We do not check for an overflow here since the shift operation results in 0 or 1.
                    uint8 v = uint8((uint256(vs) >> 255) + 27);
                    return tryRecover(hash, v, r, s);
                }
            }
            /**
             * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
             */
            function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
                (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, r, vs);
                _throwError(error, errorArg);
                return recovered;
            }
            /**
             * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
             * `r` and `s` signature fields separately.
             */
            function tryRecover(
                bytes32 hash,
                uint8 v,
                bytes32 r,
                bytes32 s
            ) internal pure returns (address, RecoverError, bytes32) {
                // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
                // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
                // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
                // signatures from current libraries generate a unique signature with an s-value in the lower half order.
                //
                // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
                // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
                // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
                // these malleable signatures as well.
                if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
                    return (address(0), RecoverError.InvalidSignatureS, s);
                }
                // If the signature is valid (and not malleable), return the signer address
                address signer = ecrecover(hash, v, r, s);
                if (signer == address(0)) {
                    return (address(0), RecoverError.InvalidSignature, bytes32(0));
                }
                return (signer, RecoverError.NoError, bytes32(0));
            }
            /**
             * @dev Overload of {ECDSA-recover} that receives the `v`,
             * `r` and `s` signature fields separately.
             */
            function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
                (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, v, r, s);
                _throwError(error, errorArg);
                return recovered;
            }
            /**
             * @dev Optionally reverts with the corresponding custom error according to the `error` argument provided.
             */
            function _throwError(RecoverError error, bytes32 errorArg) private pure {
                if (error == RecoverError.NoError) {
                    return; // no error: do nothing
                } else if (error == RecoverError.InvalidSignature) {
                    revert ECDSAInvalidSignature();
                } else if (error == RecoverError.InvalidSignatureLength) {
                    revert ECDSAInvalidSignatureLength(uint256(errorArg));
                } else if (error == RecoverError.InvalidSignatureS) {
                    revert ECDSAInvalidSignatureS(errorArg);
                }
            }
        }
        // SPDX-License-Identifier: MIT
        // OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165.sol)
        pragma solidity ^0.8.20;
        import {IERC165} from "./IERC165.sol";
        /**
         * @dev Implementation of the {IERC165} interface.
         *
         * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
         * for the additional interface id that will be supported. For example:
         *
         * ```solidity
         * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
         *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
         * }
         * ```
         */
        abstract contract ERC165 is IERC165 {
            /**
             * @dev See {IERC165-supportsInterface}.
             */
            function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
                return interfaceId == type(IERC165).interfaceId;
            }
        }
        // SPDX-License-Identifier: MIT
        // OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)
        pragma solidity ^0.8.20;
        /**
         * @dev Interface of the ERC165 standard, as defined in the
         * https://eips.ethereum.org/EIPS/eip-165[EIP].
         *
         * Implementers can declare support of contract interfaces, which can then be
         * queried by others ({ERC165Checker}).
         *
         * For an implementation, see {ERC165}.
         */
        interface IERC165 {
            /**
             * @dev Returns true if this contract implements the interface defined by
             * `interfaceId`. See the corresponding
             * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
             * to learn more about how these ids are created.
             *
             * This function call must use less than 30 000 gas.
             */
            function supportsInterface(bytes4 interfaceId) external view returns (bool);
        }
        // SPDX-License-Identifier: MIT
        // OpenZeppelin Contracts (last updated v5.0.0) (utils/Pausable.sol)
        pragma solidity ^0.8.20;
        import {Context} from "../utils/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 {
            bool private _paused;
            /**
             * @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);
            /**
             * @dev The operation failed because the contract is paused.
             */
            error EnforcedPause();
            /**
             * @dev The operation failed because the contract is not paused.
             */
            error ExpectedPause();
            /**
             * @dev Initializes the contract in unpaused state.
             */
            constructor() {
                _paused = false;
            }
            /**
             * @dev Modifier to make a function callable only when the contract is not paused.
             *
             * Requirements:
             *
             * - The contract must not be paused.
             */
            modifier whenNotPaused() {
                _requireNotPaused();
                _;
            }
            /**
             * @dev Modifier to make a function callable only when the contract is paused.
             *
             * Requirements:
             *
             * - The contract must be paused.
             */
            modifier whenPaused() {
                _requirePaused();
                _;
            }
            /**
             * @dev Returns true if the contract is paused, and false otherwise.
             */
            function paused() public view virtual returns (bool) {
                return _paused;
            }
            /**
             * @dev Throws if the contract is paused.
             */
            function _requireNotPaused() internal view virtual {
                if (paused()) {
                    revert EnforcedPause();
                }
            }
            /**
             * @dev Throws if the contract is not paused.
             */
            function _requirePaused() internal view virtual {
                if (!paused()) {
                    revert ExpectedPause();
                }
            }
            /**
             * @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());
            }
        }
        // SPDX-License-Identifier: MIT
        pragma solidity ^0.8.20;
        import "@openzeppelin/contracts/access/AccessControl.sol";
        import "@openzeppelin/contracts/utils/Pausable.sol";
        import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
        import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
        import "./interface/IDToken.sol";
        /// @title DBridge: A decentralized bridge contract for token transfers
        /// @notice This contract facilitates token transfers between different chains using a decentralized approach.
        contract DBridge is Pausable, AccessControl {
            using SafeERC20 for IERC20;
            bytes32 public constant GOVERNOR_ROLE = keccak256("GOVERNOR_ROLE");
            bytes32 public constant RELAYER_ROLE = keccak256("RELAYER_ROLE");
            bytes32 public immutable domainHash;
            address payable immutable exchangePool;
            uint256 private _bridgeIdCounter = 0;
            IDToken private rewardToken;
            uint256 private baseVoteFee;
            uint256 private rewardFee;
            /// @dev Enum representing the types of transactions (Deposit or Withdraw).
            enum TxnType {
                DEPOSIT,
                WITHDRAW
            }
            struct TokenDetails {
                mapping(uint256 => address) bridgeTokenAddress;
                uint256 platformFee;
                bool isAvailable;
                bool isMintableBurnable;
            }
            struct BridgeTxn {
                uint256 bridgeIndex;
                TxnType txnType;
                address sender;
                uint256 chainId;
                address tokenAddress;
                uint256 amount;
                bool isWithdrawed;
                bool isVerifiedByRelayer;
                address[] confirmations;
                mapping(address => uint256) reward;
            }
            struct VoteData {
                bytes32 bridgeId;
                uint256 bridgeIndex;
                address sender;
                uint256 chainId;
                uint256 sourceChainId;
                address sourceBridgeAddress;
                address tokenAddress;
                uint256 amount;
            }
            struct WithdrawData {
                bytes32 bridgeId;
                uint256 bridgeIndex;
                address sender;
                uint256 chainId;
                uint256 sourceChainId;
                address sourceBridgeAddress;
                address tokenAddress;
                uint256 amount;
                bytes[] signatures;
            }
            mapping(address => TokenDetails) public supportedTokens;
            mapping(bytes32 => BridgeTxn) public bridgeTxn;
            event Deposit(
                bytes32 indexed bridgeId,
                uint256 indexed bridgeIndex,
                address sender,
                uint256 chainId,
                address tokenAddress,
                uint256 amount
            );
            event Bridge(
                bytes32 indexed bridgeId,
                uint256 sourceChainId,
                address receiver,
                address tokenAddress,
                uint256 amount,
                address[] validators
            );
            event Confirmation(
                bytes32 indexed bridgeId,
                uint256 sourceChainId,
                address receiver,
                address tokenAddress,
                uint256 amount,
                address confirmer
            );
            event Withdraw(
                bytes32 indexed bridgeId,
                address indexed receiver,
                address bridgeTokenAddress,
                uint256 amount
            );
            /// @notice Deploy the DBridge contract with the provided parameters.
            /// @param name The name of the contract.
            /// @param version The version of the contract.
            /// @param exchangePoolAddress The address of the exchange pool.
            /// @param rewarTokenAddress The address of the reward token.
            /// @param _baseVoteFee The base vote fee.
            /// @param _rewardFee The reward fee.
            constructor(
                string memory name,
                string memory version,
                address payable exchangePoolAddress,
                IDToken rewarTokenAddress,
                uint256 _baseVoteFee,
                uint256 _rewardFee
            ) {
                _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
                _grantRole(GOVERNOR_ROLE, msg.sender);
                exchangePool = exchangePoolAddress;
                rewardToken = rewarTokenAddress;
                baseVoteFee = _baseVoteFee;
                rewardFee = _rewardFee;
                domainHash = keccak256(
                    abi.encode(
                        keccak256(
                            abi.encodePacked(
                                "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
                            )
                        ),
                        keccak256(abi.encodePacked(name)),
                        keccak256(abi.encodePacked(version)),
                        block.chainid,
                        address(this)
                    )
                );
            }
            /// @notice Pause the contract, preventing new transactions.
            function pause() public onlyRole(DEFAULT_ADMIN_ROLE) {
                _pause();
            }
            /// @notice Unpause the contract, allowing transactions to continue.
            function unpause() public onlyRole(DEFAULT_ADMIN_ROLE) {
                _unpause();
            }
            /// @notice Add a new token to the supported tokens list.
            /// @param tokenAddress The address of the token to be added.
            /// @param tokenPlatformFee The platform fee associated with the token.
            /// @param isMintableBurnableToken A flag indicating if the token is mintable/burnable.
            function addToken(
                address tokenAddress,
                uint256 tokenPlatformFee,
                bool isMintableBurnableToken
            ) external onlyRole(GOVERNOR_ROLE) whenNotPaused {
                try IDToken(tokenAddress).totalSupply() returns (uint256) {
                    try IDToken(tokenAddress).name() returns (string memory) {
                        TokenDetails storage token = supportedTokens[tokenAddress];
                        token.isAvailable = true;
                        token.platformFee = tokenPlatformFee;
                        token.isMintableBurnable = isMintableBurnableToken;
                    } catch {
                        revert("Address not token");
                    }
                } catch {
                    revert("Address not token");
                }
            }
            /// @notice Add support for a new chain for a specific token.
            /// @param tokenAddress The address of the token.
            /// @param chainId The ID of the supported chain.
            /// @param bridgeTokenAddress The address of the corresponding bridge token.
            function addSupportedChain(
                address tokenAddress,
                uint256 chainId,
                address bridgeTokenAddress
            ) external onlyRole(GOVERNOR_ROLE) whenNotPaused {
                supportedTokens[tokenAddress].bridgeTokenAddress[
                    chainId
                ] = bridgeTokenAddress;
            }
            /// @notice Enable or disable a token for transfers.
            /// @param tokenAddress The address of the token.
            /// @param isEnable Set to true to enable the token, or false to disable it.
            function enableDisableToken(
                address tokenAddress,
                bool isEnable
            ) external onlyRole(GOVERNOR_ROLE) whenNotPaused {
                supportedTokens[tokenAddress].isAvailable = isEnable;
            }
            /// @notice Edit the platform fee of an existing token.
            /// @param tokenAddress The address of the token.
            /// @param newPlatformFee The updated platform fee for the token.
            function updateTokenPlatformFee(
                address tokenAddress,
                uint256 newPlatformFee
            ) external onlyRole(GOVERNOR_ROLE) {
                require(
                    supportedTokens[tokenAddress].isAvailable,
                    "Token is not supported"
                );
                supportedTokens[tokenAddress].platformFee = newPlatformFee;
            }
            /// @notice Get the bridge token address for a specific token on a given chain.
            /// @param tokenAddress The address of the token.
            /// @param chainId The ID of the chain.
            /// @return bridgeTokenAddress The bridge token address for the token on the specified chain.
            function getBridgeTokenAddress(
                address tokenAddress,
                uint256 chainId
            ) external view whenNotPaused returns (address bridgeTokenAddress) {
                return supportedTokens[tokenAddress].bridgeTokenAddress[chainId];
            }
            /// @notice Deposit tokens into the bridge for cross-chain transfer.
            /// @param chainId The ID of the target chain.
            /// @param tokenAddress The address of the token being deposited.
            /// @param amount The amount of tokens to deposit.
            /// @return bridgeId The unique ID for the bridge transaction.
            function deposit(
                uint256 chainId,
                address tokenAddress,
                uint256 amount
            ) external whenNotPaused returns (bytes32 bridgeId) {
                require(
                    supportedTokens[tokenAddress].isAvailable,
                    "Token is not supported"
                );
                require(
                    supportedTokens[tokenAddress].bridgeTokenAddress[chainId] !=
                        address(0),
                    "Chain ID is not supported"
                );
                _bridgeIdCounter += 1;
                bridgeId = keccak256(
                    abi.encodePacked(
                        _msgSender(),
                        address(this),
                        chainId,
                        supportedTokens[tokenAddress].bridgeTokenAddress[chainId],
                        amount,
                        _bridgeIdCounter
                    )
                );
                require(
                    bridgeTxn[bridgeId].sender == address(0),
                    "Bridge already exists"
                );
                if (supportedTokens[tokenAddress].isMintableBurnable) {
                    IDToken(tokenAddress).burnFrom(_msgSender(), amount);
                } else {
                    IERC20(tokenAddress).safeTransferFrom(
                        _msgSender(),
                        address(this),
                        amount
                    );
                }
                BridgeTxn storage txn = bridgeTxn[bridgeId];
                txn.txnType = TxnType.DEPOSIT;
                txn.sender = _msgSender();
                txn.chainId = chainId;
                txn.tokenAddress = tokenAddress;
                txn.amount = amount;
                txn.isWithdrawed = false;
                txn.isVerifiedByRelayer = false;
                emit Deposit(
                    bridgeId,
                    _bridgeIdCounter,
                    _msgSender(),
                    chainId,
                    supportedTokens[tokenAddress].bridgeTokenAddress[chainId],
                    amount
                );
            }
            /// @notice Bridge a withdrawal request initiated on another chain.
            /// @param withdrawData The data for the withdrawal request.
            function bridge(WithdrawData calldata withdrawData) external whenNotPaused {
                require(hasRole(RELAYER_ROLE, _msgSender()), "Not a relayer");
                BridgeTxn storage txn = bridgeTxn[withdrawData.bridgeId];
                require(!txn.isVerifiedByRelayer, "Bridge already verified");
                bytes32 bridgeId_ = keccak256(
                    abi.encodePacked(
                        withdrawData.sender,
                        withdrawData.sourceBridgeAddress,
                        block.chainid,
                        withdrawData.tokenAddress,
                        withdrawData.amount,
                        withdrawData.bridgeIndex
                    )
                );
                require(withdrawData.bridgeId == bridgeId_, "Invalid bridge id");
                require(
                    withdrawData.signatures.length >= 3,
                    "should have 3 or more signatures"
                );
                address[] memory signers = _verifyWithdraw(withdrawData);
                for (uint256 i = 0; i < signers.length; i++) {
                    require(
                        hasRole(RELAYER_ROLE, signers[i]),
                        "Unauthorized signature"
                    );
                }
                if (txn.sender == address(0)) {
                    txn.bridgeIndex = withdrawData.bridgeIndex;
                    txn.txnType = TxnType.WITHDRAW;
                    txn.sender = withdrawData.sender;
                    txn.chainId = withdrawData.sourceChainId;
                    txn.tokenAddress = withdrawData.tokenAddress;
                    txn.amount = withdrawData.amount;
                    txn.isWithdrawed = false;
                } else {
                    require(
                        txn.txnType == TxnType.WITHDRAW,
                        "Not a withdrawal transaction"
                    );
                }
                txn.isVerifiedByRelayer = true;
                emit Bridge(
                    withdrawData.bridgeId,
                    withdrawData.sourceChainId,
                    withdrawData.sender,
                    withdrawData.tokenAddress,
                    withdrawData.amount,
                    signers
                );
            }
            /// @notice Add a new bridge transaction initiated on another chain.
            /// @param voteData The data for the bridge transaction.
            function addBridge(VoteData calldata voteData) external payable whenNotPaused {
                require(
                    supportedTokens[voteData.tokenAddress].isAvailable,
                    "Token is not supported"
                );
                bytes32 bridgeId_ = keccak256(
                    abi.encodePacked(
                        voteData.sender,
                        voteData.sourceBridgeAddress,
                        block.chainid,
                        voteData.tokenAddress,
                        voteData.amount,
                        voteData.bridgeIndex
                    )
                );
                require(voteData.bridgeId == bridgeId_, "Invalid bridge id");
                require(voteData.sender != _msgSender(), "Cannot vote for self");
                BridgeTxn storage txn = bridgeTxn[voteData.bridgeId];
                require(txn.sender == address(0), "Bridge already exists");
                uint256 rewardMultiple = msg.value / baseVoteFee;
                require(rewardMultiple > 0, "Insufficient fee");
                exchangePool.transfer(msg.value);
                txn.bridgeIndex = voteData.bridgeIndex;
                txn.txnType = TxnType.WITHDRAW;
                txn.sender = voteData.sender;
                txn.chainId = voteData.sourceChainId;
                txn.tokenAddress = voteData.tokenAddress;
                txn.amount = voteData.amount;
                txn.isWithdrawed = false;
                txn.isVerifiedByRelayer = false;
                txn.confirmations.push(_msgSender());
                txn.reward[_msgSender()] = rewardMultiple * rewardFee;
                emit Confirmation(
                    voteData.bridgeId,
                    voteData.sourceChainId,
                    voteData.sender,
                    voteData.tokenAddress,
                    voteData.amount,
                    _msgSender()
                );
            }
            /// @notice Confirm a previously initiated bridge transaction.
            /// @param voteData The data for confirming the bridge transaction.
            function confirmBridge(VoteData calldata voteData) external payable whenNotPaused {
                bytes32 bridgeId_ = keccak256(
                    abi.encodePacked(
                        voteData.sender,
                        voteData.sourceBridgeAddress,
                        voteData.chainId,
                        voteData.tokenAddress,
                        voteData.amount,
                        voteData.bridgeIndex
                    )
                );
                require(voteData.bridgeId == bridgeId_, "Invalid bridge id");
                require(voteData.sender != _msgSender(), "Cannot vote for self");
                BridgeTxn storage txn = bridgeTxn[voteData.bridgeId];
                require(txn.sender != address(0), "Bridge doesn't exist");
                require(!txn.isWithdrawed, "Bridge already withdrawn");
                require(txn.txnType == TxnType.WITHDRAW, "Not a withdrawal type");
                require(
                    txn.bridgeIndex == voteData.bridgeIndex &&
                        txn.sender == voteData.sender &&
                        txn.chainId == voteData.sourceChainId &&
                        txn.tokenAddress == voteData.tokenAddress &&
                        txn.amount == voteData.amount,
                    "Data verification failed"
                );
                uint256 rewardMultiple = msg.value / baseVoteFee;
                require(rewardMultiple > 0, "Insufficient fee");
                exchangePool.transfer(msg.value);
                txn.confirmations.push(_msgSender());
                txn.reward[_msgSender()] = rewardMultiple * rewardFee;
            }
            /// @notice Withdraw tokens from the bridge after all confirmations are received.
            /// @param bridgeId The ID of the bridge transaction to withdraw from.
            function withdraw(bytes32 bridgeId) external payable whenNotPaused {
                BridgeTxn storage txn = bridgeTxn[bridgeId];
                require(txn.sender != address(0), "Bridge doesn't exist");
                require(!txn.isWithdrawed, "Bridge already withdrawn");
                require(txn.txnType == TxnType.WITHDRAW, "Not a withdrawal type");
                require(txn.isVerifiedByRelayer, "Bridge not verified");
                require(
                    supportedTokens[txn.tokenAddress].platformFee == msg.value,
                    "Not Platform fee"
                );
                if (supportedTokens[txn.tokenAddress].platformFee > 0) {
                    exchangePool.transfer(msg.value);
                }
                txn.isWithdrawed = true;
                if (supportedTokens[txn.tokenAddress].isMintableBurnable) {
                    IDToken(txn.tokenAddress).mint(txn.sender, txn.amount);
                } else {
                    IERC20(txn.tokenAddress).safeTransfer(txn.sender, txn.amount);
                }
                for (uint256 i = 0; i < txn.confirmations.length; i++) {
                    IDToken(rewardToken).mint(
                        txn.confirmations[i],
                        txn.reward[txn.confirmations[i]]
                    );
                }
                emit Withdraw(bridgeId, txn.sender, txn.tokenAddress, txn.amount);
            }
            /**
             * @dev Verifies the withdrawal request by recovering the signers from provided ECDSA signatures.
             *
             * @param withdrawData_ The withdrawal data including the bridge ID, sender, chain ID, token address, amount, and signatures.
             * @return signers An array of addresses representing the verified signers of the withdrawal request.
             */
            function _verifyWithdraw(
                WithdrawData calldata withdrawData_
            ) internal view returns (address[] memory) {
                bytes32 hash = keccak256(
                    abi.encodePacked(
                        "\\x19\\x01",
                        domainHash,
                        keccak256(
                            abi.encode(
                                keccak256(
                                    abi.encodePacked(
                                        "BridgeToken(bytes32 bridgeId,address sender,uint256 chainId,address tokenAddress,uint256 amount)"
                                    )
                                ),
                                withdrawData_.bridgeId,
                                withdrawData_.sender,
                                block.chainid,
                                withdrawData_.tokenAddress,
                                withdrawData_.amount
                            )
                        )
                    )
                );
                // Create an array of addresses to store the recovered signers.
                address[] memory signers = new address[](
                    withdrawData_.signatures.length
                );
                // Recover signers from the provided ECDSA signatures.
                for (uint256 i = 0; i < withdrawData_.signatures.length; i++) {
                    bytes memory _signature = withdrawData_.signatures[i];
                    signers[i] = ECDSA.recover(hash, _signature);
                }
                return signers;
            }
        }
        // SPDX-License-Identifier: MIT
        pragma solidity ^0.8.20;
        interface IDToken {
            function name() external view returns (string memory);
            function totalSupply() external view returns (uint256);
            function mint(address to, uint256 amount) external;
            function burnFrom(address account, uint256 amount) external;
        }
        

        File 2 of 4: VOWToken
        // File: contracts/thirdParty/ECDSA.sol
        
        // Source: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/cryptography/ECDSA.sol
        // Line 60 added to original source in accordance with recommendation on accepting signatures with 0/1 for v
        
        pragma solidity ^0.6.0;
        
        /**
         * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
         *
         * These functions can be used to verify that a message was signed by the holder
         * of the private keys of a given address.
         */
        library ECDSA {
            /**
             * @dev Returns the address that signed a hashed message (`hash`) with
             * `signature`. This address can then be used for verification purposes.
             *
             * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
             * this function rejects them by requiring the `s` value to be in the lower
             * half order, and the `v` value to be either 27 or 28.
             *
             * IMPORTANT: `hash` _must_ be the result of a hash operation for the
             * verification to be secure: it is possible to craft signatures that
             * recover to arbitrary addresses for non-hashed data. A safe way to ensure
             * this is by receiving a hash of the original message (which may otherwise
             * be too long), and then calling {toEthSignedMessageHash} on it.
             */
            function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
                // Check the signature length
                if (signature.length != 65) {
                    revert("ECDSA: invalid signature length");
                }
        
                // Divide the signature in r, s and v variables
                bytes32 r;
                bytes32 s;
                uint8 v;
        
                // ecrecover takes the signature parameters, and the only way to get them
                // currently is to use assembly.
                // solhint-disable-next-line no-inline-assembly
                assembly {
                    r := mload(add(signature, 0x20))
                    s := mload(add(signature, 0x40))
                    v := byte(0, mload(add(signature, 0x60)))
                }
        
                // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
                // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
                // the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most
                // signatures from current libraries generate a unique signature with an s-value in the lower half order.
                //
                // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
                // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
                // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
                // these malleable signatures as well.
                if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
                    revert("ECDSA: invalid signature 's' value");
                }
        
                if (v < 27) v += 27;
        
                if (v != 27 && v != 28) {
                    revert("ECDSA: invalid signature 'v' value");
                }
        
                // If the signature is valid (and not malleable), return the signer address
                address signer = ecrecover(hash, v, r, s);
                require(signer != address(0), "ECDSA: invalid signature");
        
                return signer;
            }
        
            /**
             * @dev Returns an Ethereum Signed Message, created from a `hash`. This
             * replicates the behavior of the
             * https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign[`eth_sign`]
             * JSON-RPC method.
             *
             * See {recover}.
             */
            function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
                // 32 is the length in bytes of hash,
                // enforced by the type signature above
                return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
            }
        }
        
        // File: contracts/interfaces/IERC777.sol
        
        pragma solidity 0.6.7;
        
        // As defined in https://eips.ethereum.org/EIPS/eip-777
        interface IERC777 {
          event Sent(address indexed operator, address indexed from, address indexed to, uint256 amount, bytes data,
              bytes operatorData);
          event Minted(address indexed operator, address indexed to, uint256 amount, bytes data, bytes operatorData);
          event Burned(address indexed operator, address indexed from, uint256 amount, bytes data, bytes operatorData);
          event AuthorizedOperator(address indexed operator,address indexed holder);
          event RevokedOperator(address indexed operator, address indexed holder);
        
          function name() external view returns (string memory);
          function symbol() external view returns (string memory);
          function totalSupply() external view returns (uint256);
          function balanceOf(address holder) external view returns (uint256);
          function granularity() external view returns (uint256);
          function defaultOperators() external view returns (address[] memory);
          function isOperatorFor(address operator, address holder) external view returns (bool);
          function authorizeOperator(address operator) external;
          function revokeOperator(address operator) external;
          function send(address to, uint256 amount, bytes calldata data) external;
          function operatorSend(address from, address to, uint256 amount, bytes calldata data, bytes calldata operatorData) external;
          function burn(uint256 amount, bytes calldata data) external;
          function operatorBurn( address from, uint256 amount, bytes calldata data, bytes calldata operatorData) external;
        }
        
        // File: contracts/interfaces/IERC20.sol
        
        pragma solidity 0.6.7;
        
        // As described in https://eips.ethereum.org/EIPS/eip-20
        interface IERC20 {
          event Transfer(address indexed from, address indexed to, uint256 value);
          event Approval(address indexed owner, address indexed spender, uint256 value);
        
          function name() external view returns (string memory); // optional method - see eip spec
          function symbol() external view returns (string memory); // optional method - see eip spec
          function decimals() external view returns (uint8); // optional method - see eip spec
          function totalSupply() external view returns (uint256);
          function balanceOf(address owner) external view returns (uint256);
          function transfer(address to, uint256 value) external returns (bool);
          function transferFrom(address from, address to, uint256 value) external returns (bool);
          function approve(address spender, uint256 value) external returns (bool);
          function allowance(address owner, address spender) external view returns (uint256);
        }
        
        // File: contracts/thirdParty/interfaces/IERC1820Registry.sol
        
        // From open https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/introspection/IERC1820Registry.sol
        
        pragma solidity ^0.6.0;
        
        /**
         * @dev Interface of the global ERC1820 Registry, as defined in the
         * https://eips.ethereum.org/EIPS/eip-1820[EIP]. Accounts may register
         * implementers for interfaces in this registry, as well as query support.
         *
         * Implementers may be shared by multiple accounts, and can also implement more
         * than a single interface for each account. Contracts can implement interfaces
         * for themselves, but externally-owned accounts (EOA) must delegate this to a
         * contract.
         *
         * {IERC165} interfaces can also be queried via the registry.
         *
         * For an in-depth explanation and source code analysis, see the EIP text.
         */
        interface IERC1820Registry {
            /**
             * @dev Sets `newManager` as the manager for `account`. A manager of an
             * account is able to set interface implementers for it.
             *
             * By default, each account is its own manager. Passing a value of `0x0` in
             * `newManager` will reset the manager to this initial state.
             *
             * Emits a {ManagerChanged} event.
             *
             * Requirements:
             *
             * - the caller must be the current manager for `account`.
             */
            function setManager(address account, address newManager) external;
        
            /**
             * @dev Returns the manager for `account`.
             *
             * See {setManager}.
             */
            function getManager(address account) external view returns (address);
        
            /**
             * @dev Sets the `implementer` contract as ``account``'s implementer for
             * `interfaceHash`.
             *
             * `account` being the zero address is an alias for the caller's address.
             * The zero address can also be used in `implementer` to remove an old one.
             *
             * See {interfaceHash} to learn how these are created.
             *
             * Emits an {InterfaceImplementerSet} event.
             *
             * Requirements:
             *
             * - the caller must be the current manager for `account`.
             * - `interfaceHash` must not be an {IERC165} interface id (i.e. it must not
             * end in 28 zeroes).
             * - `implementer` must implement {IERC1820Implementer} and return true when
             * queried for support, unless `implementer` is the caller. See
             * {IERC1820Implementer-canImplementInterfaceForAddress}.
             */
            function setInterfaceImplementer(address account, bytes32 interfaceHash, address implementer) external;
        
            /**
             * @dev Returns the implementer of `interfaceHash` for `account`. If no such
             * implementer is registered, returns the zero address.
             *
             * If `interfaceHash` is an {IERC165} interface id (i.e. it ends with 28
             * zeroes), `account` will be queried for support of it.
             *
             * `account` being the zero address is an alias for the caller's address.
             */
            function getInterfaceImplementer(address account, bytes32 interfaceHash) external view returns (address);
        
            /**
             * @dev Returns the interface hash for an `interfaceName`, as defined in the
             * corresponding
             * https://eips.ethereum.org/EIPS/eip-1820#interface-name[section of the EIP].
             */
            function interfaceHash(string calldata interfaceName) external pure returns (bytes32);
        
            /**
             *  @notice Updates the cache with whether the contract implements an ERC165 interface or not.
             *  @param account Address of the contract for which to update the cache.
             *  @param interfaceId ERC165 interface for which to update the cache.
             */
            function updateERC165Cache(address account, bytes4 interfaceId) external;
        
            /**
             *  @notice Checks whether a contract implements an ERC165 interface or not.
             *  If the result is not cached a direct lookup on the contract address is performed.
             *  If the result is not cached or the cached value is out-of-date, the cache MUST be updated manually by calling
             *  {updateERC165Cache} with the contract address.
             *  @param account Address of the contract to check.
             *  @param interfaceId ERC165 interface to check.
             *  @return True if `account` implements `interfaceId`, false otherwise.
             */
            function implementsERC165Interface(address account, bytes4 interfaceId) external view returns (bool);
        
            /**
             *  @notice Checks whether a contract implements an ERC165 interface or not without using nor updating the cache.
             *  @param account Address of the contract to check.
             *  @param interfaceId ERC165 interface to check.
             *  @return True if `account` implements `interfaceId`, false otherwise.
             */
            function implementsERC165InterfaceNoCache(address account, bytes4 interfaceId) external view returns (bool);
        
            event InterfaceImplementerSet(address indexed account, bytes32 indexed interfaceHash, address indexed implementer);
        
            event ManagerChanged(address indexed account, address indexed newManager);
        }
        
        // File: contracts/interfaces/IERC777Sender.sol
        
        pragma solidity 0.6.7;
        
        // As defined in the 'ERC777TokensSender And The tokensToSend Hook' section of https://eips.ethereum.org/EIPS/eip-777
        interface IERC777Sender {
          function tokensToSend(address operator, address from, address to, uint256 amount, bytes calldata data,
              bytes calldata operatorData) external;
        }
        
        // File: contracts/interfaces/IERC777Recipient.sol
        
        pragma solidity 0.6.7;
        
        // As defined in the 'ERC777TokensRecipient And The tokensReceived Hook' section of https://eips.ethereum.org/EIPS/eip-777
        interface IERC777Recipient {
          function tokensReceived(address operator, address from, address to, uint256 amount, bytes calldata data,
              bytes calldata operatorData) external;
        }
        
        // File: contracts/thirdParty/SafeMath.sol
        
        // Source: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/math/SafeMath.sol
        
        pragma solidity ^0.6.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, 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) {
                return sub(a, b, "SafeMath: subtraction overflow");
            }
        
            /**
             * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
             * overflow (when the result is negative).
             *
             * Counterpart to Solidity's `-` operator.
             *
             * Requirements:
             * - Subtraction cannot overflow.
             *
             * _Available since v2.4.0._
             */
            function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                require(b <= a, errorMessage);
                uint256 c = a - b;
        
                return c;
            }
        
            /**
             * @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) {
                // 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 0;
                }
        
                uint256 c = a * b;
                require(c / a == b, "SafeMath: multiplication overflow");
        
                return c;
            }
        
            /**
             * @dev Returns the integer division of two unsigned integers. Reverts 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) {
                return div(a, b, "SafeMath: division by zero");
            }
        
            /**
             * @dev Returns the integer division of two unsigned integers. Reverts with custom message 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.
             *
             * _Available since v2.4.0._
             */
            function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                // Solidity only automatically asserts when dividing by 0
                require(b > 0, errorMessage);
                uint256 c = a / b;
                // assert(a == b * c + a % b); // There is no case in which this doesn't hold
        
                return c;
            }
        
            /**
             * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
             * Reverts 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) {
                return mod(a, b, "SafeMath: modulo by zero");
            }
        
            /**
             * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
             * Reverts with custom message 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.
             *
             * _Available since v2.4.0._
             */
            function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                require(b != 0, errorMessage);
                return a % b;
            }
        }
        
        // File: contracts/libraries/LToken.sol
        
        pragma solidity 0.6.7;
        
        
        
        
        
        struct TokenState {
          uint256 totalSupply;
          mapping(address => uint256) balances;
          mapping(address => mapping(address => uint256)) approvals;
          mapping(address => mapping(address => bool)) authorizedOperators;
          address[] defaultOperators;
          mapping(address => bool) defaultOperatorIsRevoked;
          mapping(address => bool) minters;
        }
        
        library LToken {
          using SafeMath for uint256;
        
          event Transfer(address indexed from, address indexed to, uint256 value);
          event Approval(address indexed owner, address indexed spender, uint256 value);
          event Sent(address indexed operator, address indexed from, address indexed to, uint256 amount, bytes data,
              bytes operatorData);
          event Minted(address indexed operator, address indexed to, uint256 amount, bytes data, bytes operatorData);
          event Burned(address indexed operator, address indexed from, uint256 amount, bytes data, bytes operatorData);
          event AuthorizedOperator(address indexed operator, address indexed holder);
          event RevokedOperator(address indexed operator, address indexed holder);
        
          // Universal address as defined in Registry Contract Address section of https://eips.ethereum.org/EIPS/eip-1820
          IERC1820Registry constant internal ERC1820_REGISTRY = IERC1820Registry(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24);
          // precalculated hashes - see https://github.com/ethereum/solidity/issues/4024
          // keccak256("ERC777TokensSender")
          bytes32 constant internal ERC777_TOKENS_SENDER_HASH = 0x29ddb589b1fb5fc7cf394961c1adf5f8c6454761adf795e67fe149f658abe895;
          // keccak256("ERC777TokensRecipient")
          bytes32 constant internal ERC777_TOKENS_RECIPIENT_HASH = 0xb281fc8c12954d22544db45de3159a39272895b169a852b314f9cc762e44c53b;
        
          modifier checkSenderNotOperator(address _operator) {
            require(_operator != msg.sender, "Cannot be operator for self");
            _;
          }
        
          function initState(TokenState storage _tokenState, uint8 _decimals, uint256 _initialSupply)
            external
          {
            _tokenState.defaultOperators.push(address(this));
            _tokenState.totalSupply = _initialSupply.mul(10**uint256(_decimals));
            _tokenState.balances[msg.sender] = _tokenState.totalSupply;
          }
        
          function transferFrom(TokenState storage _tokenState, address _from, address _to, uint256 _value)
            external
          {
            _tokenState.approvals[_from][msg.sender] = _tokenState.approvals[_from][msg.sender].sub(_value, "Amount not approved");
            doSend(_tokenState, msg.sender, _from, _to, _value, "", "", false);
          }
        
          function approve(TokenState storage _tokenState, address _spender, uint256 _value)
            external
          {
            require(_spender != address(0), "Cannot approve to zero address");
            _tokenState.approvals[msg.sender][_spender] = _value;
            emit Approval(msg.sender, _spender, _value);
          }
        
          function authorizeOperator(TokenState storage _tokenState, address _operator)
            checkSenderNotOperator(_operator)
            external
          {
            if (_operator == address(this))
              _tokenState.defaultOperatorIsRevoked[msg.sender] = false;
            else
              _tokenState.authorizedOperators[_operator][msg.sender] = true;
            emit AuthorizedOperator(_operator, msg.sender);
          }
        
          function revokeOperator(TokenState storage _tokenState, address _operator)
            checkSenderNotOperator(_operator)
            external
          {
            if (_operator == address(this))
              _tokenState.defaultOperatorIsRevoked[msg.sender] = true;
            else
              _tokenState.authorizedOperators[_operator][msg.sender] = false;
            emit RevokedOperator(_operator, msg.sender);
          }
        
          function authorizeMinter(TokenState storage _tokenState, address _minter)
            external
          {
            _tokenState.minters[_minter] = true;
          }
        
          function revokeMinter(TokenState storage _tokenState, address _minter)
            external
          {
            _tokenState.minters[_minter] = false;
          }
        
          function doMint(TokenState storage _tokenState, address _to, uint256 _amount)
            external
          {
            assert(_to != address(0));
        
            _tokenState.totalSupply = _tokenState.totalSupply.add(_amount);
            _tokenState.balances[_to] = _tokenState.balances[_to].add(_amount);
        
            // From ERC777: The token contract MUST call the tokensReceived hook after updating the state.
            receiveHook(address(this), address(0), _to, _amount, "", "", true);
        
            emit Minted(address(this), _to, _amount, "", "");
            emit Transfer(address(0), _to, _amount);
          }
        
          function doBurn(TokenState storage _tokenState, address _operator, address _from, uint256 _amount, bytes calldata _data,
              bytes calldata _operatorData)
            external
          {
            assert(_from != address(0));
            // From ERC777: The token contract MUST call the tokensToSend hook before updating the state.
            sendHook(_operator, _from, address(0), _amount, _data, _operatorData);
        
            _tokenState.balances[_from] = _tokenState.balances[_from].sub(_amount, "Cannot burn more than balance");
            _tokenState.totalSupply = _tokenState.totalSupply.sub(_amount);
        
            emit Burned(_operator, _from, _amount, _data, _operatorData);
            emit Transfer(_from, address(0), _amount);
          }
        
          function doSend(TokenState storage _tokenState, address _operator, address _from, address _to, uint256 _amount,
              bytes memory _data, bytes memory _operatorData, bool _enforceERC777)
            public
          {
            assert(_from != address(0));
        
            require(_to != address(0), "Cannot send funds to 0 address");
            // From ERC777: The token contract MUST call the tokensToSend hook before updating the state.
            sendHook(_operator, _from, _to, _amount, _data, _operatorData);
        
            _tokenState.balances[_from] = _tokenState.balances[_from].sub(_amount, "Amount exceeds available funds");
            _tokenState.balances[_to] = _tokenState.balances[_to].add(_amount);
        
            emit Sent(_operator, _from, _to, _amount, _data, _operatorData);
            emit Transfer(_from, _to, _amount);
        
            // From ERC777: The token contract MUST call the tokensReceived hook after updating the state.
            receiveHook(_operator, _from, _to, _amount, _data, _operatorData, _enforceERC777);
          }
        
          function receiveHook(address _operator, address _from, address _to, uint256 _amount, bytes memory _data,
              bytes memory _operatorData, bool _enforceERC777)
            public
          {
            address implementer = ERC1820_REGISTRY.getInterfaceImplementer(_to, ERC777_TOKENS_RECIPIENT_HASH);
            if (implementer != address(0))
              IERC777Recipient(implementer).tokensReceived(_operator, _from, _to, _amount, _data, _operatorData);
            else if (_enforceERC777)
              require(!isContract(_to), "Must be registered with ERC1820");
          }
        
          function sendHook(address _operator, address _from, address _to, uint256 _amount, bytes memory _data,
              bytes memory _operatorData)
            public
          {
            address implementer = ERC1820_REGISTRY.getInterfaceImplementer(_from, ERC777_TOKENS_SENDER_HASH);
            if (implementer != address(0))
              IERC777Sender(implementer).tokensToSend(_operator, _from, _to, _amount, _data, _operatorData);
          }
        
          function isContract(address _account)
            private
            view
            returns (bool isContract_)
          {
            uint256 size;
        
            assembly {
              size := extcodesize(_account)
            }
        
            isContract_ = size != 0;
          }
        }
        
        // File: contracts/Token.sol
        
        pragma solidity 0.6.7;
        
        
        
        
        /**
         * Implements ERC777 with ERC20 as defined in https://eips.ethereum.org/EIPS/eip-777, with minting support.
         * NOTE: Minting is internal only: derive from this contract according to usage.
         */
        contract Token is IERC777, IERC20 {
        
          string private tokenName;
          string private tokenSymbol;
          uint8 constant private tokenDecimals = 18;
          uint256 constant private tokenGranularity = 1;
          TokenState public tokenState;
        
          // Universal address as defined in Registry Contract Address section of https://eips.ethereum.org/EIPS/eip-1820
          IERC1820Registry constant internal ERC1820_REGISTRY = IERC1820Registry(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24);
          // keccak256("ERC777Token")
          bytes32 constant internal ERC777_TOKEN_HASH = 0xac7fbab5f54a3ca8194167523c6753bfeb96a445279294b6125b68cce2177054;
          // keccak256("ERC20Token")
          bytes32 constant internal ERC20_TOKEN_HASH = 0xaea199e31a596269b42cdafd93407f14436db6e4cad65417994c2eb37381e05a;
        
          event AuthorizedMinter(address minter);
          event RevokedMinter(address minter);
        
          constructor(string memory _name, string memory _symbol, uint256 _initialSupply)
            internal
          {
            require(bytes(_name).length != 0, "Needs a name");
            require(bytes(_symbol).length != 0, "Needs a symbol");
            tokenName = _name;
            tokenSymbol = _symbol;
            LToken.initState(tokenState, tokenDecimals, _initialSupply);
        
            ERC1820_REGISTRY.setInterfaceImplementer(address(this), ERC777_TOKEN_HASH, address(this));
            ERC1820_REGISTRY.setInterfaceImplementer(address(this), ERC20_TOKEN_HASH, address(this));
          }
        
          modifier onlyOperator(address _holder) {
            require(isOperatorFor(msg.sender, _holder), "Not an operator");
            _;
          }
        
          modifier onlyMinter {
            require(tokenState.minters[msg.sender], "onlyMinter");
            _;
          }
        
          function name()
            external
            view
            override(IERC777, IERC20)
            returns (string memory name_)
          {
            name_ = tokenName;
          }
        
          function symbol()
            external
            view
            override(IERC777, IERC20)
            returns (string memory symbol_)
          {
            symbol_ = tokenSymbol;
          }
        
          function decimals()
            external
            view
            override
            returns (uint8 decimals_)
          {
            decimals_ = tokenDecimals;
          }
        
          function granularity()
            external
            view
            override
            returns (uint256 granularity_)
          {
            granularity_ = tokenGranularity;
          }
        
          function balanceOf(address _holder)
            external
            override(IERC777, IERC20)
            view
            returns (uint256 balance_)
          {
            balance_ = tokenState.balances[_holder];
          }
        
          function transfer(address _to, uint256 _value)
            external
            override
            returns (bool success_)
          {
            doSend(msg.sender, msg.sender, _to, _value, "", "", false);
            success_ = true;
          }
        
          function transferFrom(address _from, address _to, uint256 _value)
            external
            override
            returns (bool success_)
          {
            LToken.transferFrom(tokenState, _from, _to, _value);
            success_ = true;
          }
        
          function approve(address _spender, uint256 _value)
            external
            override
            returns (bool success_)
          {
            LToken.approve(tokenState, _spender, _value);
            success_ = true;
          }
        
          function allowance(address _holder, address _spender)
            external
            view
            override
            returns (uint256 remaining_)
          {
            remaining_ = tokenState.approvals[_holder][_spender];
          }
        
          function defaultOperators()
            external
            view
            override
            returns (address[] memory)
          {
            return tokenState.defaultOperators;
          }
        
          function authorizeOperator(address _operator)
            external
            override
          {
            LToken.authorizeOperator(tokenState, _operator);
          }
        
          function revokeOperator(address _operator)
            external
            override
          {
            LToken.revokeOperator(tokenState, _operator);
          }
        
          function send(address _to, uint256 _amount, bytes calldata _data)
            external
            override
          {
            doSend(msg.sender, msg.sender, _to, _amount, _data, "", true);
          }
        
          function operatorSend(address _from, address _to, uint256 _amount, bytes calldata _data, bytes calldata _operatorData)
            external
            override
            onlyOperator(_from)
          {
            doSend(msg.sender, _from, _to, _amount, _data, _operatorData, true);
          }
        
          function burn(uint256 _amount, bytes calldata _data)
            external
            override
          {
            doBurn(msg.sender, msg.sender, _amount, _data, "");
          }
        
          function operatorBurn(address _from, uint256 _amount, bytes calldata _data, bytes calldata _operatorData)
            external
            override
            onlyOperator(_from)
          {
            doBurn(msg.sender, _from, _amount, _data, _operatorData);
          }
        
          function mint(address _to, uint256 _amount)
            external
            onlyMinter
          {
            LToken.doMint(tokenState, _to, _amount);
          }
        
          function totalSupply()
            external
            view
            override(IERC777, IERC20)
            returns (uint256 totalSupply_)
          {
            totalSupply_ = tokenState.totalSupply;
          }
        
          function isOperatorFor(address _operator, address _holder)
            public
            view
            override
            returns (bool isOperatorFor_)
          {
            isOperatorFor_ = (_operator == _holder || tokenState.authorizedOperators[_operator][_holder]
                || _operator == address(this) && !tokenState.defaultOperatorIsRevoked[_holder]);
          }
        
          function doSend(address _operator, address _from, address _to, uint256 _amount, bytes memory _data,
              bytes memory _operatorData, bool _enforceERC777)
            internal
            virtual
          {
            LToken.doSend(tokenState, _operator, _from, _to, _amount, _data, _operatorData, _enforceERC777);
          }
        
          function doBurn(address _operator, address _from, uint256 _amount, bytes memory _data, bytes memory _operatorData)
            internal
          {
            LToken.doBurn(tokenState, _operator, _from, _amount, _data, _operatorData);
          }
        
          function authorizeMinter(address _minter)
            internal
          {
            LToken.authorizeMinter(tokenState, _minter);
        
            emit AuthorizedMinter(_minter);
          }
        
          function revokeMinter(address _minter)
            internal
          {
            LToken.revokeMinter(tokenState, _minter);
        
            emit RevokedMinter(_minter);
          }
        }
        
        // File: contracts/Owned.sol
        
        pragma solidity 0.6.7;
        
        contract Owned {
        
          address public owner = msg.sender;
        
          event LogOwnershipTransferred(address indexed owner, address indexed newOwner);
        
          modifier onlyOwner {
            require(msg.sender == owner, "Sender must be owner");
            _;
          }
        
          function setOwner(address _owner)
            external
            onlyOwner
          {
            require(_owner != address(0), "Owner cannot be zero address");
            emit LogOwnershipTransferred(owner, _owner);
            owner = _owner;
          }
        }
        
        // File: contracts/VOWToken.sol
        
        pragma solidity 0.6.7;
        
        
        
        
        /**
         * ERC777/20 contract which also:
         * - is owned
         * - supports proxying of own tokens (only if signed correctly)
         * - supports partner contracts, keyed by hash
         * - supports minting (only by owner approved contracts)
         * - has a USD price
         */
        contract VOWToken is Token, IERC777Recipient, Owned {
        
          mapping (bytes32 => bool) public proxyProofs;
          uint256[2] public usdRate;
          address public usdRateSetter;
          mapping(bytes32 => address payable) public partnerContracts;
        
          // precalculated hash - see https://github.com/ethereum/solidity/issues/4024
          // keccak256("ERC777TokensRecipient")
          bytes32 constant internal ERC777_TOKENS_RECIPIENT_HASH = 0xb281fc8c12954d22544db45de3159a39272895b169a852b314f9cc762e44c53b;
        
          event LogUSDRateSetterSet(address indexed usdRateSetter);
          event LogUSDRateSet(uint256 numTokens, uint256 numUSD);
          event LogProxiedTokens(address indexed from, address indexed to, uint256 amount, bytes data, uint256 nonce, bytes proof);
          event LogPartnerContractSet(bytes32 indexed keyHash, address indexed partnerContract);
          event LogMintPermissionSet(address indexed contractAddress, bool canMint);
        
          constructor(string memory _name, string memory _symbol, uint256 _initialSupply, uint256[2] memory _initialUSDRate)
            public
            Token(_name, _symbol, _initialSupply)
          {
            doSetUSDRate(_initialUSDRate[0], _initialUSDRate[1]);
        
            ERC1820_REGISTRY.setInterfaceImplementer(address(this), ERC777_TOKENS_RECIPIENT_HASH, address(this));
          }
        
          modifier onlyUSDRateSetter() {
            require(msg.sender == usdRateSetter, "onlyUSDRateSetter");
            _;
          }
        
          modifier onlyOwnTokens {
            require(msg.sender == address(this), "onlyOwnTokens");
            _;
          }
        
          modifier addressNotNull(address _address) {
            require(_address != address(0), "Address cannot be null");
            _;
          }
        
          function tokensReceived(address /* _operator */, address /* _from */, address /* _to */, uint256 _amount,
              bytes calldata _data, bytes calldata /* _operatorData */)
            external
            override
            onlyOwnTokens
          {
            (address from, address to, uint256 amount, bytes memory data, uint256 nonce, bytes memory proof) =
                abi.decode(_data, (address, address, uint256, bytes, uint256, bytes));
            checkProxying(from, to, amount, data, nonce, proof);
        
            if (_amount != 0)
              this.send(from, _amount, "");
        
            this.operatorSend(from, to, amount, data, _data);
        
            emit LogProxiedTokens(from, to, amount, data, nonce, proof);
          }
        
          function setPartnerContract(bytes32 _keyHash, address payable _partnerContract)
            external
            onlyOwner
            addressNotNull(_partnerContract)
          {
            require(_keyHash != bytes32(0), "Missing key hash");
            partnerContracts[_keyHash] = _partnerContract;
        
            emit LogPartnerContractSet(_keyHash, _partnerContract);
          }
        
          function setUSDRateSetter(address _usdRateSetter)
            external
            onlyOwner
            addressNotNull(_usdRateSetter)
          {
            usdRateSetter = _usdRateSetter;
        
            emit LogUSDRateSetterSet(_usdRateSetter);
          }
        
          function setUSDRate(uint256 _numTokens, uint256 _numUSD)
            external
            onlyUSDRateSetter
          {
            doSetUSDRate(_numTokens, _numUSD);
        
            emit LogUSDRateSet(_numTokens, _numUSD);
          }
        
          function setMintPermission(address _contract, bool _canMint)
            external
            onlyOwner
            addressNotNull(_contract)
          {
            if (_canMint)
              authorizeMinter(_contract);
            else
              revokeMinter(_contract);
        
            emit LogMintPermissionSet(_contract, _canMint);
          }
        
          function doSetUSDRate(uint256 _numTokens, uint256 _numUSD)
            private
          {
            require(_numTokens != 0, "numTokens cannot be zero");
            require(_numUSD != 0, "numUSD cannot be zero");
            usdRate = [_numTokens, _numUSD];
          }
        
          function checkProxying(address _from, address _to, uint256 _amount, bytes memory _data, uint256 _nonce, bytes memory _proof)
            private
          {
            require(!proxyProofs[keccak256(_proof)], "Proxy proof not unique");
            proxyProofs[keccak256(_proof)] = true;
            bytes32 hash = keccak256(abi.encodePacked(address(this), _from, _to, _amount, _data, _nonce));
            address signer = ECDSA.recover(ECDSA.toEthSignedMessageHash(hash), _proof);
            require(signer == _from, "Bad signer");
          }
        }

        File 3 of 4: LToken
        // File: contracts\thirdParty\interfaces\IERC1820Registry.sol
        
        // From open https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/introspection/IERC1820Registry.sol
        
        pragma solidity ^0.6.0;
        
        /**
         * @dev Interface of the global ERC1820 Registry, as defined in the
         * https://eips.ethereum.org/EIPS/eip-1820[EIP]. Accounts may register
         * implementers for interfaces in this registry, as well as query support.
         *
         * Implementers may be shared by multiple accounts, and can also implement more
         * than a single interface for each account. Contracts can implement interfaces
         * for themselves, but externally-owned accounts (EOA) must delegate this to a
         * contract.
         *
         * {IERC165} interfaces can also be queried via the registry.
         *
         * For an in-depth explanation and source code analysis, see the EIP text.
         */
        interface IERC1820Registry {
            /**
             * @dev Sets `newManager` as the manager for `account`. A manager of an
             * account is able to set interface implementers for it.
             *
             * By default, each account is its own manager. Passing a value of `0x0` in
             * `newManager` will reset the manager to this initial state.
             *
             * Emits a {ManagerChanged} event.
             *
             * Requirements:
             *
             * - the caller must be the current manager for `account`.
             */
            function setManager(address account, address newManager) external;
        
            /**
             * @dev Returns the manager for `account`.
             *
             * See {setManager}.
             */
            function getManager(address account) external view returns (address);
        
            /**
             * @dev Sets the `implementer` contract as ``account``'s implementer for
             * `interfaceHash`.
             *
             * `account` being the zero address is an alias for the caller's address.
             * The zero address can also be used in `implementer` to remove an old one.
             *
             * See {interfaceHash} to learn how these are created.
             *
             * Emits an {InterfaceImplementerSet} event.
             *
             * Requirements:
             *
             * - the caller must be the current manager for `account`.
             * - `interfaceHash` must not be an {IERC165} interface id (i.e. it must not
             * end in 28 zeroes).
             * - `implementer` must implement {IERC1820Implementer} and return true when
             * queried for support, unless `implementer` is the caller. See
             * {IERC1820Implementer-canImplementInterfaceForAddress}.
             */
            function setInterfaceImplementer(address account, bytes32 interfaceHash, address implementer) external;
        
            /**
             * @dev Returns the implementer of `interfaceHash` for `account`. If no such
             * implementer is registered, returns the zero address.
             *
             * If `interfaceHash` is an {IERC165} interface id (i.e. it ends with 28
             * zeroes), `account` will be queried for support of it.
             *
             * `account` being the zero address is an alias for the caller's address.
             */
            function getInterfaceImplementer(address account, bytes32 interfaceHash) external view returns (address);
        
            /**
             * @dev Returns the interface hash for an `interfaceName`, as defined in the
             * corresponding
             * https://eips.ethereum.org/EIPS/eip-1820#interface-name[section of the EIP].
             */
            function interfaceHash(string calldata interfaceName) external pure returns (bytes32);
        
            /**
             *  @notice Updates the cache with whether the contract implements an ERC165 interface or not.
             *  @param account Address of the contract for which to update the cache.
             *  @param interfaceId ERC165 interface for which to update the cache.
             */
            function updateERC165Cache(address account, bytes4 interfaceId) external;
        
            /**
             *  @notice Checks whether a contract implements an ERC165 interface or not.
             *  If the result is not cached a direct lookup on the contract address is performed.
             *  If the result is not cached or the cached value is out-of-date, the cache MUST be updated manually by calling
             *  {updateERC165Cache} with the contract address.
             *  @param account Address of the contract to check.
             *  @param interfaceId ERC165 interface to check.
             *  @return True if `account` implements `interfaceId`, false otherwise.
             */
            function implementsERC165Interface(address account, bytes4 interfaceId) external view returns (bool);
        
            /**
             *  @notice Checks whether a contract implements an ERC165 interface or not without using nor updating the cache.
             *  @param account Address of the contract to check.
             *  @param interfaceId ERC165 interface to check.
             *  @return True if `account` implements `interfaceId`, false otherwise.
             */
            function implementsERC165InterfaceNoCache(address account, bytes4 interfaceId) external view returns (bool);
        
            event InterfaceImplementerSet(address indexed account, bytes32 indexed interfaceHash, address indexed implementer);
        
            event ManagerChanged(address indexed account, address indexed newManager);
        }
        
        // File: contracts\interfaces\IERC777Sender.sol
        
        pragma solidity 0.6.7;
        
        // As defined in the 'ERC777TokensSender And The tokensToSend Hook' section of https://eips.ethereum.org/EIPS/eip-777
        interface IERC777Sender {
          function tokensToSend(address operator, address from, address to, uint256 amount, bytes calldata data,
              bytes calldata operatorData) external;
        }
        
        // File: contracts\interfaces\IERC777Recipient.sol
        
        pragma solidity 0.6.7;
        
        // As defined in the 'ERC777TokensRecipient And The tokensReceived Hook' section of https://eips.ethereum.org/EIPS/eip-777
        interface IERC777Recipient {
          function tokensReceived(address operator, address from, address to, uint256 amount, bytes calldata data,
              bytes calldata operatorData) external;
        }
        
        // File: contracts\thirdParty\SafeMath.sol
        
        // Source: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/math/SafeMath.sol
        
        pragma solidity ^0.6.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, 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) {
                return sub(a, b, "SafeMath: subtraction overflow");
            }
        
            /**
             * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
             * overflow (when the result is negative).
             *
             * Counterpart to Solidity's `-` operator.
             *
             * Requirements:
             * - Subtraction cannot overflow.
             *
             * _Available since v2.4.0._
             */
            function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                require(b <= a, errorMessage);
                uint256 c = a - b;
        
                return c;
            }
        
            /**
             * @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) {
                // 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 0;
                }
        
                uint256 c = a * b;
                require(c / a == b, "SafeMath: multiplication overflow");
        
                return c;
            }
        
            /**
             * @dev Returns the integer division of two unsigned integers. Reverts 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) {
                return div(a, b, "SafeMath: division by zero");
            }
        
            /**
             * @dev Returns the integer division of two unsigned integers. Reverts with custom message 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.
             *
             * _Available since v2.4.0._
             */
            function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                // Solidity only automatically asserts when dividing by 0
                require(b > 0, errorMessage);
                uint256 c = a / b;
                // assert(a == b * c + a % b); // There is no case in which this doesn't hold
        
                return c;
            }
        
            /**
             * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
             * Reverts 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) {
                return mod(a, b, "SafeMath: modulo by zero");
            }
        
            /**
             * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
             * Reverts with custom message 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.
             *
             * _Available since v2.4.0._
             */
            function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                require(b != 0, errorMessage);
                return a % b;
            }
        }
        
        // File: contracts\libraries\LToken.sol
        
        pragma solidity 0.6.7;
        
        
        
        
        
        struct TokenState {
          uint256 totalSupply;
          mapping(address => uint256) balances;
          mapping(address => mapping(address => uint256)) approvals;
          mapping(address => mapping(address => bool)) authorizedOperators;
          address[] defaultOperators;
          mapping(address => bool) defaultOperatorIsRevoked;
          mapping(address => bool) minters;
        }
        
        library LToken {
          using SafeMath for uint256;
        
          event Transfer(address indexed from, address indexed to, uint256 value);
          event Approval(address indexed owner, address indexed spender, uint256 value);
          event Sent(address indexed operator, address indexed from, address indexed to, uint256 amount, bytes data,
              bytes operatorData);
          event Minted(address indexed operator, address indexed to, uint256 amount, bytes data, bytes operatorData);
          event Burned(address indexed operator, address indexed from, uint256 amount, bytes data, bytes operatorData);
          event AuthorizedOperator(address indexed operator, address indexed holder);
          event RevokedOperator(address indexed operator, address indexed holder);
        
          // Universal address as defined in Registry Contract Address section of https://eips.ethereum.org/EIPS/eip-1820
          IERC1820Registry constant internal ERC1820_REGISTRY = IERC1820Registry(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24);
          // precalculated hashes - see https://github.com/ethereum/solidity/issues/4024
          // keccak256("ERC777TokensSender")
          bytes32 constant internal ERC777_TOKENS_SENDER_HASH = 0x29ddb589b1fb5fc7cf394961c1adf5f8c6454761adf795e67fe149f658abe895;
          // keccak256("ERC777TokensRecipient")
          bytes32 constant internal ERC777_TOKENS_RECIPIENT_HASH = 0xb281fc8c12954d22544db45de3159a39272895b169a852b314f9cc762e44c53b;
        
          modifier checkSenderNotOperator(address _operator) {
            require(_operator != msg.sender, "Cannot be operator for self");
            _;
          }
        
          function initState(TokenState storage _tokenState, uint8 _decimals, uint256 _initialSupply)
            external
          {
            _tokenState.defaultOperators.push(address(this));
            _tokenState.totalSupply = _initialSupply.mul(10**uint256(_decimals));
            _tokenState.balances[msg.sender] = _tokenState.totalSupply;
          }
        
          function transferFrom(TokenState storage _tokenState, address _from, address _to, uint256 _value)
            external
          {
            _tokenState.approvals[_from][msg.sender] = _tokenState.approvals[_from][msg.sender].sub(_value, "Amount not approved");
            doSend(_tokenState, msg.sender, _from, _to, _value, "", "", false);
          }
        
          function approve(TokenState storage _tokenState, address _spender, uint256 _value)
            external
          {
            require(_spender != address(0), "Cannot approve to zero address");
            _tokenState.approvals[msg.sender][_spender] = _value;
            emit Approval(msg.sender, _spender, _value);
          }
        
          function authorizeOperator(TokenState storage _tokenState, address _operator)
            checkSenderNotOperator(_operator)
            external
          {
            if (_operator == address(this))
              _tokenState.defaultOperatorIsRevoked[msg.sender] = false;
            else
              _tokenState.authorizedOperators[_operator][msg.sender] = true;
            emit AuthorizedOperator(_operator, msg.sender);
          }
        
          function revokeOperator(TokenState storage _tokenState, address _operator)
            checkSenderNotOperator(_operator)
            external
          {
            if (_operator == address(this))
              _tokenState.defaultOperatorIsRevoked[msg.sender] = true;
            else
              _tokenState.authorizedOperators[_operator][msg.sender] = false;
            emit RevokedOperator(_operator, msg.sender);
          }
        
          function authorizeMinter(TokenState storage _tokenState, address _minter)
            external
          {
            _tokenState.minters[_minter] = true;
          }
        
          function revokeMinter(TokenState storage _tokenState, address _minter)
            external
          {
            _tokenState.minters[_minter] = false;
          }
        
          function doMint(TokenState storage _tokenState, address _to, uint256 _amount)
            external
          {
            assert(_to != address(0));
        
            _tokenState.totalSupply = _tokenState.totalSupply.add(_amount);
            _tokenState.balances[_to] = _tokenState.balances[_to].add(_amount);
        
            // From ERC777: The token contract MUST call the tokensReceived hook after updating the state.
            receiveHook(address(this), address(0), _to, _amount, "", "", true);
        
            emit Minted(address(this), _to, _amount, "", "");
            emit Transfer(address(0), _to, _amount);
          }
        
          function doBurn(TokenState storage _tokenState, address _operator, address _from, uint256 _amount, bytes calldata _data,
              bytes calldata _operatorData)
            external
          {
            assert(_from != address(0));
            // From ERC777: The token contract MUST call the tokensToSend hook before updating the state.
            sendHook(_operator, _from, address(0), _amount, _data, _operatorData);
        
            _tokenState.balances[_from] = _tokenState.balances[_from].sub(_amount, "Cannot burn more than balance");
            _tokenState.totalSupply = _tokenState.totalSupply.sub(_amount);
        
            emit Burned(_operator, _from, _amount, _data, _operatorData);
            emit Transfer(_from, address(0), _amount);
          }
        
          function doSend(TokenState storage _tokenState, address _operator, address _from, address _to, uint256 _amount,
              bytes memory _data, bytes memory _operatorData, bool _enforceERC777)
            public
          {
            assert(_from != address(0));
        
            require(_to != address(0), "Cannot send funds to 0 address");
            // From ERC777: The token contract MUST call the tokensToSend hook before updating the state.
            sendHook(_operator, _from, _to, _amount, _data, _operatorData);
        
            _tokenState.balances[_from] = _tokenState.balances[_from].sub(_amount, "Amount exceeds available funds");
            _tokenState.balances[_to] = _tokenState.balances[_to].add(_amount);
        
            emit Sent(_operator, _from, _to, _amount, _data, _operatorData);
            emit Transfer(_from, _to, _amount);
        
            // From ERC777: The token contract MUST call the tokensReceived hook after updating the state.
            receiveHook(_operator, _from, _to, _amount, _data, _operatorData, _enforceERC777);
          }
        
          function receiveHook(address _operator, address _from, address _to, uint256 _amount, bytes memory _data,
              bytes memory _operatorData, bool _enforceERC777)
            public
          {
            address implementer = ERC1820_REGISTRY.getInterfaceImplementer(_to, ERC777_TOKENS_RECIPIENT_HASH);
            if (implementer != address(0))
              IERC777Recipient(implementer).tokensReceived(_operator, _from, _to, _amount, _data, _operatorData);
            else if (_enforceERC777)
              require(!isContract(_to), "Must be registered with ERC1820");
          }
        
          function sendHook(address _operator, address _from, address _to, uint256 _amount, bytes memory _data,
              bytes memory _operatorData)
            public
          {
            address implementer = ERC1820_REGISTRY.getInterfaceImplementer(_from, ERC777_TOKENS_SENDER_HASH);
            if (implementer != address(0))
              IERC777Sender(implementer).tokensToSend(_operator, _from, _to, _amount, _data, _operatorData);
          }
        
          function isContract(address _account)
            private
            view
            returns (bool isContract_)
          {
            uint256 size;
        
            assembly {
              size := extcodesize(_account)
            }
        
            isContract_ = size != 0;
          }
        }

        File 4 of 4: ERC1820Registry
        /* ERC1820 Pseudo-introspection Registry Contract
         * This standard defines a universal registry smart contract where any address (contract or regular account) can
         * register which interface it supports and which smart contract is responsible for its implementation.
         *
         * Written in 2019 by Jordi Baylina and Jacques Dafflon
         *
         * To the extent possible under law, the author(s) have dedicated all copyright and related and neighboring rights to
         * this software to the public domain worldwide. This software is distributed without any warranty.
         *
         * You should have received a copy of the CC0 Public Domain Dedication along with this software. If not, see
         * <http://creativecommons.org/publicdomain/zero/1.0/>.
         *
         *    ███████╗██████╗  ██████╗ ██╗ █████╗ ██████╗  ██████╗
         *    ██╔════╝██╔══██╗██╔════╝███║██╔══██╗╚════██╗██╔═████╗
         *    █████╗  ██████╔╝██║     ╚██║╚█████╔╝ █████╔╝██║██╔██║
         *    ██╔══╝  ██╔══██╗██║      ██║██╔══██╗██╔═══╝ ████╔╝██║
         *    ███████╗██║  ██║╚██████╗ ██║╚█████╔╝███████╗╚██████╔╝
         *    ╚══════╝╚═╝  ╚═╝ ╚═════╝ ╚═╝ ╚════╝ ╚══════╝ ╚═════╝
         *
         *    ██████╗ ███████╗ ██████╗ ██╗███████╗████████╗██████╗ ██╗   ██╗
         *    ██╔══██╗██╔════╝██╔════╝ ██║██╔════╝╚══██╔══╝██╔══██╗╚██╗ ██╔╝
         *    ██████╔╝█████╗  ██║  ███╗██║███████╗   ██║   ██████╔╝ ╚████╔╝
         *    ██╔══██╗██╔══╝  ██║   ██║██║╚════██║   ██║   ██╔══██╗  ╚██╔╝
         *    ██║  ██║███████╗╚██████╔╝██║███████║   ██║   ██║  ██║   ██║
         *    ╚═╝  ╚═╝╚══════╝ ╚═════╝ ╚═╝╚══════╝   ╚═╝   ╚═╝  ╚═╝   ╚═╝
         *
         */
        pragma solidity 0.5.3;
        // IV is value needed to have a vanity address starting with '0x1820'.
        // IV: 53759
        
        /// @dev The interface a contract MUST implement if it is the implementer of
        /// some (other) interface for any address other than itself.
        interface ERC1820ImplementerInterface {
            /// @notice Indicates whether the contract implements the interface 'interfaceHash' for the address 'addr' or not.
            /// @param interfaceHash keccak256 hash of the name of the interface
            /// @param addr Address for which the contract will implement the interface
            /// @return ERC1820_ACCEPT_MAGIC only if the contract implements 'interfaceHash' for the address 'addr'.
            function canImplementInterfaceForAddress(bytes32 interfaceHash, address addr) external view returns(bytes32);
        }
        
        
        /// @title ERC1820 Pseudo-introspection Registry Contract
        /// @author Jordi Baylina and Jacques Dafflon
        /// @notice This contract is the official implementation of the ERC1820 Registry.
        /// @notice For more details, see https://eips.ethereum.org/EIPS/eip-1820
        contract ERC1820Registry {
            /// @notice ERC165 Invalid ID.
            bytes4 constant internal INVALID_ID = 0xffffffff;
            /// @notice Method ID for the ERC165 supportsInterface method (= `bytes4(keccak256('supportsInterface(bytes4)'))`).
            bytes4 constant internal ERC165ID = 0x01ffc9a7;
            /// @notice Magic value which is returned if a contract implements an interface on behalf of some other address.
            bytes32 constant internal ERC1820_ACCEPT_MAGIC = keccak256(abi.encodePacked("ERC1820_ACCEPT_MAGIC"));
        
            /// @notice mapping from addresses and interface hashes to their implementers.
            mapping(address => mapping(bytes32 => address)) internal interfaces;
            /// @notice mapping from addresses to their manager.
            mapping(address => address) internal managers;
            /// @notice flag for each address and erc165 interface to indicate if it is cached.
            mapping(address => mapping(bytes4 => bool)) internal erc165Cached;
        
            /// @notice Indicates a contract is the 'implementer' of 'interfaceHash' for 'addr'.
            event InterfaceImplementerSet(address indexed addr, bytes32 indexed interfaceHash, address indexed implementer);
            /// @notice Indicates 'newManager' is the address of the new manager for 'addr'.
            event ManagerChanged(address indexed addr, address indexed newManager);
        
            /// @notice Query if an address implements an interface and through which contract.
            /// @param _addr Address being queried for the implementer of an interface.
            /// (If '_addr' is the zero address then 'msg.sender' is assumed.)
            /// @param _interfaceHash Keccak256 hash of the name of the interface as a string.
            /// E.g., 'web3.utils.keccak256("ERC777TokensRecipient")' for the 'ERC777TokensRecipient' interface.
            /// @return The address of the contract which implements the interface '_interfaceHash' for '_addr'
            /// or '0' if '_addr' did not register an implementer for this interface.
            function getInterfaceImplementer(address _addr, bytes32 _interfaceHash) external view returns (address) {
                address addr = _addr == address(0) ? msg.sender : _addr;
                if (isERC165Interface(_interfaceHash)) {
                    bytes4 erc165InterfaceHash = bytes4(_interfaceHash);
                    return implementsERC165Interface(addr, erc165InterfaceHash) ? addr : address(0);
                }
                return interfaces[addr][_interfaceHash];
            }
        
            /// @notice Sets the contract which implements a specific interface for an address.
            /// Only the manager defined for that address can set it.
            /// (Each address is the manager for itself until it sets a new manager.)
            /// @param _addr Address for which to set the interface.
            /// (If '_addr' is the zero address then 'msg.sender' is assumed.)
            /// @param _interfaceHash Keccak256 hash of the name of the interface as a string.
            /// E.g., 'web3.utils.keccak256("ERC777TokensRecipient")' for the 'ERC777TokensRecipient' interface.
            /// @param _implementer Contract address implementing '_interfaceHash' for '_addr'.
            function setInterfaceImplementer(address _addr, bytes32 _interfaceHash, address _implementer) external {
                address addr = _addr == address(0) ? msg.sender : _addr;
                require(getManager(addr) == msg.sender, "Not the manager");
        
                require(!isERC165Interface(_interfaceHash), "Must not be an ERC165 hash");
                if (_implementer != address(0) && _implementer != msg.sender) {
                    require(
                        ERC1820ImplementerInterface(_implementer)
                            .canImplementInterfaceForAddress(_interfaceHash, addr) == ERC1820_ACCEPT_MAGIC,
                        "Does not implement the interface"
                    );
                }
                interfaces[addr][_interfaceHash] = _implementer;
                emit InterfaceImplementerSet(addr, _interfaceHash, _implementer);
            }
        
            /// @notice Sets '_newManager' as manager for '_addr'.
            /// The new manager will be able to call 'setInterfaceImplementer' for '_addr'.
            /// @param _addr Address for which to set the new manager.
            /// @param _newManager Address of the new manager for 'addr'. (Pass '0x0' to reset the manager to '_addr'.)
            function setManager(address _addr, address _newManager) external {
                require(getManager(_addr) == msg.sender, "Not the manager");
                managers[_addr] = _newManager == _addr ? address(0) : _newManager;
                emit ManagerChanged(_addr, _newManager);
            }
        
            /// @notice Get the manager of an address.
            /// @param _addr Address for which to return the manager.
            /// @return Address of the manager for a given address.
            function getManager(address _addr) public view returns(address) {
                // By default the manager of an address is the same address
                if (managers[_addr] == address(0)) {
                    return _addr;
                } else {
                    return managers[_addr];
                }
            }
        
            /// @notice Compute the keccak256 hash of an interface given its name.
            /// @param _interfaceName Name of the interface.
            /// @return The keccak256 hash of an interface name.
            function interfaceHash(string calldata _interfaceName) external pure returns(bytes32) {
                return keccak256(abi.encodePacked(_interfaceName));
            }
        
            /* --- ERC165 Related Functions --- */
            /* --- Developed in collaboration with William Entriken. --- */
        
            /// @notice Updates the cache with whether the contract implements an ERC165 interface or not.
            /// @param _contract Address of the contract for which to update the cache.
            /// @param _interfaceId ERC165 interface for which to update the cache.
            function updateERC165Cache(address _contract, bytes4 _interfaceId) external {
                interfaces[_contract][_interfaceId] = implementsERC165InterfaceNoCache(
                    _contract, _interfaceId) ? _contract : address(0);
                erc165Cached[_contract][_interfaceId] = true;
            }
        
            /// @notice Checks whether a contract implements an ERC165 interface or not.
            //  If the result is not cached a direct lookup on the contract address is performed.
            //  If the result is not cached or the cached value is out-of-date, the cache MUST be updated manually by calling
            //  'updateERC165Cache' with the contract address.
            /// @param _contract Address of the contract to check.
            /// @param _interfaceId ERC165 interface to check.
            /// @return True if '_contract' implements '_interfaceId', false otherwise.
            function implementsERC165Interface(address _contract, bytes4 _interfaceId) public view returns (bool) {
                if (!erc165Cached[_contract][_interfaceId]) {
                    return implementsERC165InterfaceNoCache(_contract, _interfaceId);
                }
                return interfaces[_contract][_interfaceId] == _contract;
            }
        
            /// @notice Checks whether a contract implements an ERC165 interface or not without using nor updating the cache.
            /// @param _contract Address of the contract to check.
            /// @param _interfaceId ERC165 interface to check.
            /// @return True if '_contract' implements '_interfaceId', false otherwise.
            function implementsERC165InterfaceNoCache(address _contract, bytes4 _interfaceId) public view returns (bool) {
                uint256 success;
                uint256 result;
        
                (success, result) = noThrowCall(_contract, ERC165ID);
                if (success == 0 || result == 0) {
                    return false;
                }
        
                (success, result) = noThrowCall(_contract, INVALID_ID);
                if (success == 0 || result != 0) {
                    return false;
                }
        
                (success, result) = noThrowCall(_contract, _interfaceId);
                if (success == 1 && result == 1) {
                    return true;
                }
                return false;
            }
        
            /// @notice Checks whether the hash is a ERC165 interface (ending with 28 zeroes) or not.
            /// @param _interfaceHash The hash to check.
            /// @return True if '_interfaceHash' is an ERC165 interface (ending with 28 zeroes), false otherwise.
            function isERC165Interface(bytes32 _interfaceHash) internal pure returns (bool) {
                return _interfaceHash & 0x00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0;
            }
        
            /// @dev Make a call on a contract without throwing if the function does not exist.
            function noThrowCall(address _contract, bytes4 _interfaceId)
                internal view returns (uint256 success, uint256 result)
            {
                bytes4 erc165ID = ERC165ID;
        
                assembly {
                    let x := mload(0x40)               // Find empty storage location using "free memory pointer"
                    mstore(x, erc165ID)                // Place signature at beginning of empty storage
                    mstore(add(x, 0x04), _interfaceId) // Place first argument directly next to signature
        
                    success := staticcall(
                        30000,                         // 30k gas
                        _contract,                     // To addr
                        x,                             // Inputs are stored at location x
                        0x24,                          // Inputs are 36 (4 + 32) bytes long
                        x,                             // Store output over input (saves space)
                        0x20                           // Outputs are 32 bytes long
                    )
        
                    result := mload(x)                 // Load the result
                }
            }
        }