ETH Price: $2,247.82 (-0.87%)

Transaction Decoder

Block:
20647984 at Aug-31-2024 10:33:59 AM +UTC
Transaction Fee:
0.000140525321709142 ETH $0.32
Gas Used:
128,182 Gas / 1.096295281 Gwei

Emitted Events:

43 L1_ERC20_Bridge.WithdrawalBonded( transferId=0265CC49CCD2109735FAECE4BFEA944A4D168A8CF72767E7D7CB61B581A0FB6C, amount=156094538006050657476 )
44 MaticToken.Transfer( from=[Receiver] L1_ERC20_Bridge, to=0x7892a230429fFED593a8966c4f28a91dDfb178E9, value=154310082525828638743 )
45 MaticToken.Transfer( from=[Receiver] L1_ERC20_Bridge, to=[Sender] 0xd8781ca9163e9f132a4d8392332e64115688013a, value=1784455480222018733 )

Account State Difference:

  Address   Before After State Difference Code
0x22B1Cbb8...A76Eb1cc2
(Hop Protocol: MATIC Bridge)
(Lido: Execution Layer Rewards Vault)
52.814540861575157839 Eth52.814549834315157839 Eth0.00000897274
0x7D1AfA7B...8AaCfeBB0
0xd8781cA9...15688013a
(Hop Protocol: Matic Bonder)
9.788939047183323857 Eth
Nonce: 29419
9.788798521861614715 Eth
Nonce: 29420
0.000140525321709142

Execution Trace

L1_ERC20_Bridge.bondWithdrawal( recipient=0x7892a230429fFED593a8966c4f28a91dDfb178E9, amount=156094538006050657476, transferNonce=0D47784D3B7225D96D7226DEBA169983F9526EF10BC9283966985D5F75D67E0B, bonderFee=1784455480222018733 )
  • MaticToken.transfer( to=0x7892a230429fFED593a8966c4f28a91dDfb178E9, value=154310082525828638743 ) => ( True )
  • MaticToken.transfer( to=0xd8781cA9163E9f132A4D8392332E64115688013a, value=1784455480222018733 ) => ( True )
    bondWithdrawal[Bridge (ln:1058)]
    File 1 of 2: L1_ERC20_Bridge
    // SPDX-License-Identifier: MIT
    pragma solidity 0.6.12;
    pragma experimental ABIEncoderV2;
    import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
    import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
    import "./L1_Bridge.sol";
    /**
     * @dev A L1_Bridge that uses an ERC20 as the canonical token
     */
    contract L1_ERC20_Bridge is L1_Bridge {
        using SafeERC20 for IERC20;
        IERC20 public immutable l1CanonicalToken;
        constructor (IERC20 _l1CanonicalToken, address[] memory bonders, address _governance) public L1_Bridge(bonders, _governance) {
            l1CanonicalToken = _l1CanonicalToken;
        }
        /* ========== Override Functions ========== */
        function _transferFromBridge(address recipient, uint256 amount) internal override {
            l1CanonicalToken.safeTransfer(recipient, amount);
        }
        function _transferToBridge(address from, uint256 amount) internal override {
            l1CanonicalToken.safeTransferFrom(from, address(this), amount);
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity >=0.6.0 <0.8.0;
    /**
     * @dev Interface of the ERC20 standard as defined in the EIP.
     */
    interface IERC20 {
        /**
         * @dev Returns the amount of tokens in existence.
         */
        function totalSupply() external view returns (uint256);
        /**
         * @dev Returns the amount of tokens owned by `account`.
         */
        function balanceOf(address account) external view returns (uint256);
        /**
         * @dev Moves `amount` tokens from the caller's account to `recipient`.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * Emits a {Transfer} event.
         */
        function transfer(address recipient, uint256 amount) external returns (bool);
        /**
         * @dev Returns the remaining number of tokens that `spender` will be
         * allowed to spend on behalf of `owner` through {transferFrom}. This is
         * zero by default.
         *
         * This value changes when {approve} or {transferFrom} are called.
         */
        function allowance(address owner, address spender) external view returns (uint256);
        /**
         * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * IMPORTANT: Beware that changing an allowance with this method brings the risk
         * that someone may use both the old and the new allowance by unfortunate
         * transaction ordering. One possible solution to mitigate this race
         * condition is to first reduce the spender's allowance to 0 and set the
         * desired value afterwards:
         * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
         *
         * Emits an {Approval} event.
         */
        function approve(address spender, uint256 amount) external returns (bool);
        /**
         * @dev Moves `amount` tokens from `sender` to `recipient` using the
         * allowance mechanism. `amount` is then deducted from the caller's
         * allowance.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * Emits a {Transfer} event.
         */
        function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
        /**
         * @dev Emitted when `value` tokens are moved from one account (`from`) to
         * another (`to`).
         *
         * Note that `value` may be zero.
         */
        event Transfer(address indexed from, address indexed to, uint256 value);
        /**
         * @dev Emitted when the allowance of a `spender` for an `owner` is set by
         * a call to {approve}. `value` is the new allowance.
         */
        event Approval(address indexed owner, address indexed spender, uint256 value);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity >=0.6.0 <0.8.0;
    import "./IERC20.sol";
    import "../../math/SafeMath.sol";
    import "../../utils/Address.sol";
    /**
     * @title SafeERC20
     * @dev Wrappers around ERC20 operations that throw on failure (when the token
     * contract returns false). Tokens that return no value (and instead revert or
     * throw on failure) are also supported, non-reverting calls are assumed to be
     * successful.
     * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
     * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
     */
    library SafeERC20 {
        using SafeMath for uint256;
        using Address for address;
        function safeTransfer(IERC20 token, address to, uint256 value) internal {
            _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
        }
        function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
            _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
        }
        /**
         * @dev Deprecated. This function has issues similar to the ones found in
         * {IERC20-approve}, and its usage is discouraged.
         *
         * Whenever possible, use {safeIncreaseAllowance} and
         * {safeDecreaseAllowance} instead.
         */
        function safeApprove(IERC20 token, address spender, uint256 value) internal {
            // safeApprove should only be called when setting an initial allowance,
            // or when resetting it to zero. To increase and decrease it, use
            // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
            // solhint-disable-next-line max-line-length
            require((value == 0) || (token.allowance(address(this), spender) == 0),
                "SafeERC20: approve from non-zero to non-zero allowance"
            );
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
        }
        function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
            uint256 newAllowance = token.allowance(address(this), spender).add(value);
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
        }
        function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
            uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero");
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
        }
        /**
         * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
         * on the return value: the return value is optional (but if data is returned, it must not be false).
         * @param token The token targeted by the call.
         * @param data The call data (encoded using abi.encode or one of its variants).
         */
        function _callOptionalReturn(IERC20 token, bytes memory data) private {
            // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
            // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
            // the target address contains contract code and also asserts for success in the low-level call.
            bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
            if (returndata.length > 0) { // Return data is optional
                // solhint-disable-next-line max-line-length
                require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
            }
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.6.12;
    pragma experimental ABIEncoderV2;
    import "./Bridge.sol";
    import "../interfaces/IMessengerWrapper.sol";
    /**
     * @dev L1_Bridge is responsible for the bonding and challenging of TransferRoots. All TransferRoots
     * originate in the L1_Bridge through `bondTransferRoot` and are propagated up to destination L2s.
     */
    abstract contract L1_Bridge is Bridge {
        struct TransferBond {
            address bonder;
            uint256 createdAt;
            uint256 totalAmount;
            uint256 challengeStartTime;
            address challenger;
            bool challengeResolved;
        }
        /* ========== State ========== */
        mapping(uint256 => mapping(bytes32 => uint256)) public transferRootCommittedAt;
        mapping(bytes32 => TransferBond) public transferBonds;
        mapping(uint256 => mapping(address => uint256)) public timeSlotToAmountBonded;
        mapping(uint256 => uint256) public chainBalance;
        /* ========== Config State ========== */
        address public governance;
        mapping(uint256 => IMessengerWrapper) public crossDomainMessengerWrappers;
        mapping(uint256 => bool) public isChainIdPaused;
        uint256 public challengePeriod = 1 days;
        uint256 public challengeResolutionPeriod = 10 days;
        uint256 public minTransferRootBondDelay = 15 minutes;
        
        uint256 public constant CHALLENGE_AMOUNT_DIVISOR = 10;
        uint256 public constant TIME_SLOT_SIZE = 4 hours;
        /* ========== Events ========== */
        event TransferSentToL2(
            uint256 indexed chainId,
            address indexed recipient,
            uint256 amount,
            uint256 amountOutMin,
            uint256 deadline,
            address indexed relayer,
            uint256 relayerFee
        );
        event TransferRootBonded (
            bytes32 indexed root,
            uint256 amount
        );
        event TransferRootConfirmed(
            uint256 indexed originChainId,
            uint256 indexed destinationChainId,
            bytes32 indexed rootHash,
            uint256 totalAmount
        );
        event TransferBondChallenged(
            bytes32 indexed transferRootId,
            bytes32 indexed rootHash,
            uint256 originalAmount
        );
        event ChallengeResolved(
            bytes32 indexed transferRootId,
            bytes32 indexed rootHash,
            uint256 originalAmount
        );
        /* ========== Modifiers ========== */
        modifier onlyL2Bridge(uint256 chainId) {
            IMessengerWrapper messengerWrapper = crossDomainMessengerWrappers[chainId];
            messengerWrapper.verifySender(msg.sender, msg.data);
            _;
        }
        constructor (address[] memory bonders, address _governance) public Bridge(bonders) {
            governance = _governance;
        }
        /* ========== Send Functions ========== */
        /**
         * @notice `amountOutMin` and `deadline` should be 0 when no swap is intended at the destination.
         * @notice `amount` is the total amount the user wants to send including the relayer fee
         * @dev Send tokens to a supported layer-2 to mint hToken and optionally swap the hToken in the
         * AMM at the destination.
         * @param chainId The chainId of the destination chain
         * @param recipient The address receiving funds at the destination
         * @param amount The amount being sent
         * @param amountOutMin The minimum amount received after attempting to swap in the destination
         * AMM market. 0 if no swap is intended.
         * @param deadline The deadline for swapping in the destination AMM market. 0 if no
         * swap is intended.
         * @param relayer The address of the relayer at the destination.
         * @param relayerFee The amount distributed to the relayer at the destination. This is subtracted from the `amount`.
         */
        function sendToL2(
            uint256 chainId,
            address recipient,
            uint256 amount,
            uint256 amountOutMin,
            uint256 deadline,
            address relayer,
            uint256 relayerFee
        )
            external
            payable
        {
            IMessengerWrapper messengerWrapper = crossDomainMessengerWrappers[chainId];
            require(messengerWrapper != IMessengerWrapper(0), "L1_BRG: chainId not supported");
            require(isChainIdPaused[chainId] == false, "L1_BRG: Sends to this chainId are paused");
            require(amount > 0, "L1_BRG: Must transfer a non-zero amount");
            require(amount >= relayerFee, "L1_BRG: Relayer fee cannot exceed amount");
            _transferToBridge(msg.sender, amount);
            bytes memory message = abi.encodeWithSignature(
                "distribute(address,uint256,uint256,uint256,address,uint256)",
                recipient,
                amount,
                amountOutMin,
                deadline,
                relayer,
                relayerFee
            );
            chainBalance[chainId] = chainBalance[chainId].add(amount);
            messengerWrapper.sendCrossDomainMessage(message);
            emit TransferSentToL2(
                chainId,
                recipient,
                amount,
                amountOutMin,
                deadline,
                relayer,
                relayerFee
            );
        }
        /* ========== TransferRoot Functions ========== */
        /**
         * @dev Setting a TransferRoot is a two step process.
         * @dev   1. The TransferRoot is bonded with `bondTransferRoot`. Withdrawals can now begin on L1
         * @dev      and recipient L2's
         * @dev   2. The TransferRoot is confirmed after `confirmTransferRoot` is called by the l2 bridge
         * @dev      where the TransferRoot originated.
         */
        /**
         * @dev Used by the Bonder to bond a TransferRoot and propagate it up to destination L2s
         * @param rootHash The Merkle root of the TransferRoot Merkle tree
         * @param destinationChainId The id of the destination chain
         * @param totalAmount The amount destined for the destination chain
         */
        function bondTransferRoot(
            bytes32 rootHash,
            uint256 destinationChainId,
            uint256 totalAmount
        )
            external
            onlyBonder
            requirePositiveBalance
        {
            bytes32 transferRootId = getTransferRootId(rootHash, totalAmount);
            require(transferRootCommittedAt[destinationChainId][transferRootId] == 0, "L1_BRG: TransferRoot has already been confirmed");
            require(transferBonds[transferRootId].createdAt == 0, "L1_BRG: TransferRoot has already been bonded");
            uint256 currentTimeSlot = getTimeSlot(block.timestamp);
            uint256 bondAmount = getBondForTransferAmount(totalAmount);
            timeSlotToAmountBonded[currentTimeSlot][msg.sender] = timeSlotToAmountBonded[currentTimeSlot][msg.sender].add(bondAmount);
            transferBonds[transferRootId] = TransferBond(
                msg.sender,
                block.timestamp,
                totalAmount,
                uint256(0),
                address(0),
                false
            );
            _distributeTransferRoot(rootHash, destinationChainId, totalAmount);
            emit TransferRootBonded(rootHash, totalAmount);
        }
        /**
         * @dev Used by an L2 bridge to confirm a TransferRoot via cross-domain message. Once a TransferRoot
         * has been confirmed, any challenge against that TransferRoot can be resolved as unsuccessful.
         * @param originChainId The id of the origin chain
         * @param rootHash The Merkle root of the TransferRoot Merkle tree
         * @param destinationChainId The id of the destination chain
         * @param totalAmount The amount destined for each destination chain
         * @param rootCommittedAt The block timestamp when the TransferRoot was committed on its origin chain
         */
        function confirmTransferRoot(
            uint256 originChainId,
            bytes32 rootHash,
            uint256 destinationChainId,
            uint256 totalAmount,
            uint256 rootCommittedAt
        )
            external
            onlyL2Bridge(originChainId)
        {
            bytes32 transferRootId = getTransferRootId(rootHash, totalAmount);
            require(transferRootCommittedAt[destinationChainId][transferRootId] == 0, "L1_BRG: TransferRoot already confirmed");
            require(rootCommittedAt > 0, "L1_BRG: rootCommittedAt must be greater than 0");
            transferRootCommittedAt[destinationChainId][transferRootId] = rootCommittedAt;
            chainBalance[originChainId] = chainBalance[originChainId].sub(totalAmount, "L1_BRG: Amount exceeds chainBalance. This indicates a layer-2 failure.");
            // If the TransferRoot was never bonded, distribute the TransferRoot.
            TransferBond storage transferBond = transferBonds[transferRootId];
            if (transferBond.createdAt == 0) {
                _distributeTransferRoot(rootHash, destinationChainId, totalAmount);
            }
            emit TransferRootConfirmed(originChainId, destinationChainId, rootHash, totalAmount);
        }
        function _distributeTransferRoot(
            bytes32 rootHash,
            uint256 chainId,
            uint256 totalAmount
        )
            internal
        {
            // Set TransferRoot on recipient Bridge
            if (chainId == getChainId()) {
                // Set L1 TransferRoot
                _setTransferRoot(rootHash, totalAmount);
            } else {
                chainBalance[chainId] = chainBalance[chainId].add(totalAmount);
                IMessengerWrapper messengerWrapper = crossDomainMessengerWrappers[chainId];
                require(messengerWrapper != IMessengerWrapper(0), "L1_BRG: chainId not supported");
                // Set L2 TransferRoot
                bytes memory setTransferRootMessage = abi.encodeWithSignature(
                    "setTransferRoot(bytes32,uint256)",
                    rootHash,
                    totalAmount
                );
                messengerWrapper.sendCrossDomainMessage(setTransferRootMessage);
            }
        }
        /* ========== External TransferRoot Challenges ========== */
        /**
         * @dev Challenge a TransferRoot believed to be fraudulent
         * @param rootHash The Merkle root of the TransferRoot Merkle tree
         * @param originalAmount The total amount bonded for this TransferRoot
         * @param destinationChainId The id of the destination chain
         */
        function challengeTransferBond(bytes32 rootHash, uint256 originalAmount, uint256 destinationChainId) external payable {
            bytes32 transferRootId = getTransferRootId(rootHash, originalAmount);
            TransferBond storage transferBond = transferBonds[transferRootId];
            require(transferRootCommittedAt[destinationChainId][transferRootId] == 0, "L1_BRG: TransferRoot has already been confirmed");
            require(transferBond.createdAt != 0, "L1_BRG: TransferRoot has not been bonded");
            uint256 challengePeriodEnd = transferBond.createdAt.add(challengePeriod);
            require(challengePeriodEnd >= block.timestamp, "L1_BRG: TransferRoot cannot be challenged after challenge period");
            require(transferBond.challengeStartTime == 0, "L1_BRG: TransferRoot already challenged");
            transferBond.challengeStartTime = block.timestamp;
            transferBond.challenger = msg.sender;
            // Move amount from timeSlotToAmountBonded to debit
            uint256 timeSlot = getTimeSlot(transferBond.createdAt);
            uint256 bondAmount = getBondForTransferAmount(originalAmount);
            address bonder = transferBond.bonder;
            timeSlotToAmountBonded[timeSlot][bonder] = timeSlotToAmountBonded[timeSlot][bonder].sub(bondAmount);
            _addDebit(transferBond.bonder, bondAmount);
            // Get stake for challenge
            uint256 challengeStakeAmount = getChallengeAmountForTransferAmount(originalAmount);
            _transferToBridge(msg.sender, challengeStakeAmount);
            emit TransferBondChallenged(transferRootId, rootHash, originalAmount);
        }
        /**
         * @dev Resolve a challenge after the `challengeResolutionPeriod` has passed
         * @param rootHash The Merkle root of the TransferRoot Merkle tree
         * @param originalAmount The total amount originally bonded for this TransferRoot
         * @param destinationChainId The id of the destination chain
         */
        function resolveChallenge(bytes32 rootHash, uint256 originalAmount, uint256 destinationChainId) external {
            bytes32 transferRootId = getTransferRootId(rootHash, originalAmount);
            TransferBond storage transferBond = transferBonds[transferRootId];
            require(transferBond.challengeStartTime != 0, "L1_BRG: TransferRoot has not been challenged");
            require(block.timestamp > transferBond.challengeStartTime.add(challengeResolutionPeriod), "L1_BRG: Challenge period has not ended");
            require(transferBond.challengeResolved == false, "L1_BRG: TransferRoot already resolved");
            transferBond.challengeResolved = true;
            uint256 challengeStakeAmount = getChallengeAmountForTransferAmount(originalAmount);
            if (transferRootCommittedAt[destinationChainId][transferRootId] > 0) {
                // Invalid challenge
                if (transferBond.createdAt > transferRootCommittedAt[destinationChainId][transferRootId].add(minTransferRootBondDelay)) {
                    // Credit the bonder back with the bond amount plus the challenger's stake
                    _addCredit(transferBond.bonder, getBondForTransferAmount(originalAmount).add(challengeStakeAmount));
                } else {
                    // If the TransferRoot was bonded before it was committed, the challenger and Bonder
                    // get their stake back. This discourages Bonders from tricking challengers into
                    // challenging a valid TransferRoots that haven't yet been committed. It also ensures
                    // that Bonders are not punished if a TransferRoot is bonded too soon in error.
                    // Return the challenger's stake
                    _addCredit(transferBond.challenger, challengeStakeAmount);
                    // Credit the bonder back with the bond amount
                    _addCredit(transferBond.bonder, getBondForTransferAmount(originalAmount));
                }
            } else {
                // Valid challenge
                // Burn 25% of the challengers stake
                _transferFromBridge(address(0xdead), challengeStakeAmount.mul(1).div(4));
                // Reward challenger with the remaining 75% of their stake plus 100% of the Bonder's stake
                _addCredit(transferBond.challenger, challengeStakeAmount.mul(7).div(4));
            }
            emit ChallengeResolved(transferRootId, rootHash, originalAmount);
        }
        /* ========== Override Functions ========== */
        function _additionalDebit(address bonder) internal view override returns (uint256) {
            uint256 currentTimeSlot = getTimeSlot(block.timestamp);
            uint256 bonded = 0;
            uint256 numTimeSlots = challengePeriod / TIME_SLOT_SIZE;
            for (uint256 i = 0; i < numTimeSlots; i++) {
                bonded = bonded.add(timeSlotToAmountBonded[currentTimeSlot - i][bonder]);
            }
            return bonded;
        }
        function _requireIsGovernance() internal override {
            require(governance == msg.sender, "L1_BRG: Caller is not the owner");
        }
        /* ========== External Config Management Setters ========== */
        function setGovernance(address _newGovernance) external onlyGovernance {
            require(_newGovernance != address(0), "L1_BRG: _newGovernance cannot be address(0)");
            governance = _newGovernance;
        }
        function setCrossDomainMessengerWrapper(uint256 chainId, IMessengerWrapper _crossDomainMessengerWrapper) external onlyGovernance {
            crossDomainMessengerWrappers[chainId] = _crossDomainMessengerWrapper;
        }
        function setChainIdDepositsPaused(uint256 chainId, bool isPaused) external onlyGovernance {
            isChainIdPaused[chainId] = isPaused;
        }
        function setChallengePeriod(uint256 _challengePeriod) external onlyGovernance {
            require(_challengePeriod % TIME_SLOT_SIZE == 0, "L1_BRG: challengePeriod must be divisible by TIME_SLOT_SIZE");
            challengePeriod = _challengePeriod;
        }
        function setChallengeResolutionPeriod(uint256 _challengeResolutionPeriod) external onlyGovernance {
            challengeResolutionPeriod = _challengeResolutionPeriod;
        }
        function setMinTransferRootBondDelay(uint256 _minTransferRootBondDelay) external onlyGovernance {
            minTransferRootBondDelay = _minTransferRootBondDelay;
        }
        /* ========== Public Getters ========== */
        function getBondForTransferAmount(uint256 amount) public pure returns (uint256) {
            // Bond covers amount plus a bounty to pay a potential challenger
            return amount.add(getChallengeAmountForTransferAmount(amount));
        }
        function getChallengeAmountForTransferAmount(uint256 amount) public pure returns (uint256) {
            // Bond covers amount plus a bounty to pay a potential challenger
            return amount.div(CHALLENGE_AMOUNT_DIVISOR);
        }
        function getTimeSlot(uint256 time) public pure returns (uint256) {
            return time / TIME_SLOT_SIZE;
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity >=0.6.0 <0.8.0;
    /**
     * @dev Wrappers over Solidity's arithmetic operations with added overflow
     * checks.
     *
     * Arithmetic operations in Solidity wrap on overflow. This can easily result
     * in bugs, because programmers usually assume that an overflow raises an
     * error, which is the standard behavior in high level programming languages.
     * `SafeMath` restores this intuition by reverting the transaction when an
     * operation overflows.
     *
     * Using this library instead of the unchecked operations eliminates an entire
     * class of bugs, so it's recommended to use it always.
     */
    library SafeMath {
        /**
         * @dev Returns the addition of two unsigned integers, with an overflow flag.
         *
         * _Available since v3.4._
         */
        function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
            uint256 c = a + b;
            if (c < a) return (false, 0);
            return (true, c);
        }
        /**
         * @dev Returns the substraction of two unsigned integers, with an overflow flag.
         *
         * _Available since v3.4._
         */
        function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
            if (b > a) return (false, 0);
            return (true, a - b);
        }
        /**
         * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
         *
         * _Available since v3.4._
         */
        function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
            // benefit is lost if 'b' is also tested.
            // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
            if (a == 0) return (true, 0);
            uint256 c = a * b;
            if (c / a != b) return (false, 0);
            return (true, c);
        }
        /**
         * @dev Returns the division of two unsigned integers, with a division by zero flag.
         *
         * _Available since v3.4._
         */
        function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
            if (b == 0) return (false, 0);
            return (true, a / b);
        }
        /**
         * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
         *
         * _Available since v3.4._
         */
        function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
            if (b == 0) return (false, 0);
            return (true, a % b);
        }
        /**
         * @dev Returns the addition of two unsigned integers, reverting on
         * overflow.
         *
         * Counterpart to Solidity's `+` operator.
         *
         * Requirements:
         *
         * - Addition cannot overflow.
         */
        function add(uint256 a, uint256 b) internal pure returns (uint256) {
            uint256 c = a + b;
            require(c >= a, "SafeMath: addition overflow");
            return c;
        }
        /**
         * @dev Returns the subtraction of two unsigned integers, reverting on
         * overflow (when the result is negative).
         *
         * Counterpart to Solidity's `-` operator.
         *
         * Requirements:
         *
         * - Subtraction cannot overflow.
         */
        function sub(uint256 a, uint256 b) internal pure returns (uint256) {
            require(b <= a, "SafeMath: subtraction overflow");
            return a - b;
        }
        /**
         * @dev Returns the multiplication of two unsigned integers, reverting on
         * overflow.
         *
         * Counterpart to Solidity's `*` operator.
         *
         * Requirements:
         *
         * - Multiplication cannot overflow.
         */
        function mul(uint256 a, uint256 b) internal pure returns (uint256) {
            if (a == 0) return 0;
            uint256 c = a * b;
            require(c / a == b, "SafeMath: multiplication overflow");
            return c;
        }
        /**
         * @dev Returns the integer division of two unsigned integers, reverting on
         * division by zero. The result is rounded towards zero.
         *
         * Counterpart to Solidity's `/` operator. Note: this function uses a
         * `revert` opcode (which leaves remaining gas untouched) while Solidity
         * uses an invalid opcode to revert (consuming all remaining gas).
         *
         * Requirements:
         *
         * - The divisor cannot be zero.
         */
        function div(uint256 a, uint256 b) internal pure returns (uint256) {
            require(b > 0, "SafeMath: division by zero");
            return a / b;
        }
        /**
         * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
         * reverting when dividing by zero.
         *
         * Counterpart to Solidity's `%` operator. This function uses a `revert`
         * opcode (which leaves remaining gas untouched) while Solidity uses an
         * invalid opcode to revert (consuming all remaining gas).
         *
         * Requirements:
         *
         * - The divisor cannot be zero.
         */
        function mod(uint256 a, uint256 b) internal pure returns (uint256) {
            require(b > 0, "SafeMath: modulo by zero");
            return a % b;
        }
        /**
         * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
         * overflow (when the result is negative).
         *
         * CAUTION: This function is deprecated because it requires allocating memory for the error
         * message unnecessarily. For custom revert reasons use {trySub}.
         *
         * Counterpart to Solidity's `-` operator.
         *
         * Requirements:
         *
         * - Subtraction cannot overflow.
         */
        function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
            require(b <= a, errorMessage);
            return a - b;
        }
        /**
         * @dev Returns the integer division of two unsigned integers, reverting with custom message on
         * division by zero. The result is rounded towards zero.
         *
         * CAUTION: This function is deprecated because it requires allocating memory for the error
         * message unnecessarily. For custom revert reasons use {tryDiv}.
         *
         * Counterpart to Solidity's `/` operator. Note: this function uses a
         * `revert` opcode (which leaves remaining gas untouched) while Solidity
         * uses an invalid opcode to revert (consuming all remaining gas).
         *
         * Requirements:
         *
         * - The divisor cannot be zero.
         */
        function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
            require(b > 0, errorMessage);
            return a / b;
        }
        /**
         * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
         * reverting with custom message when dividing by zero.
         *
         * CAUTION: This function is deprecated because it requires allocating memory for the error
         * message unnecessarily. For custom revert reasons use {tryMod}.
         *
         * Counterpart to Solidity's `%` operator. This function uses a `revert`
         * opcode (which leaves remaining gas untouched) while Solidity uses an
         * invalid opcode to revert (consuming all remaining gas).
         *
         * Requirements:
         *
         * - The divisor cannot be zero.
         */
        function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
            require(b > 0, errorMessage);
            return a % b;
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity >=0.6.2 <0.8.0;
    /**
     * @dev Collection of functions related to the address type
     */
    library Address {
        /**
         * @dev Returns true if `account` is a contract.
         *
         * [IMPORTANT]
         * ====
         * It is unsafe to assume that an address for which this function returns
         * false is an externally-owned account (EOA) and not a contract.
         *
         * Among others, `isContract` will return false for the following
         * types of addresses:
         *
         *  - an externally-owned account
         *  - a contract in construction
         *  - an address where a contract will be created
         *  - an address where a contract lived, but was destroyed
         * ====
         */
        function isContract(address account) internal view returns (bool) {
            // This method relies on extcodesize, which returns 0 for contracts in
            // construction, since the code is only stored at the end of the
            // constructor execution.
            uint256 size;
            // solhint-disable-next-line no-inline-assembly
            assembly { size := extcodesize(account) }
            return size > 0;
        }
        /**
         * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
         * `recipient`, forwarding all available gas and reverting on errors.
         *
         * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
         * of certain opcodes, possibly making contracts go over the 2300 gas limit
         * imposed by `transfer`, making them unable to receive funds via
         * `transfer`. {sendValue} removes this limitation.
         *
         * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
         *
         * IMPORTANT: because control is transferred to `recipient`, care must be
         * taken to not create reentrancy vulnerabilities. Consider using
         * {ReentrancyGuard} or the
         * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
         */
        function sendValue(address payable recipient, uint256 amount) internal {
            require(address(this).balance >= amount, "Address: insufficient balance");
            // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
            (bool success, ) = recipient.call{ value: amount }("");
            require(success, "Address: unable to send value, recipient may have reverted");
        }
        /**
         * @dev Performs a Solidity function call using a low level `call`. A
         * plain`call` is an unsafe replacement for a function call: use this
         * function instead.
         *
         * If `target` reverts with a revert reason, it is bubbled up by this
         * function (like regular Solidity function calls).
         *
         * Returns the raw returned data. To convert to the expected return value,
         * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
         *
         * Requirements:
         *
         * - `target` must be a contract.
         * - calling `target` with `data` must not revert.
         *
         * _Available since v3.1._
         */
        function functionCall(address target, bytes memory data) internal returns (bytes memory) {
          return functionCall(target, data, "Address: low-level call failed");
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
         * `errorMessage` as a fallback revert reason when `target` reverts.
         *
         * _Available since v3.1._
         */
        function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
            return functionCallWithValue(target, data, 0, errorMessage);
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but also transferring `value` wei to `target`.
         *
         * Requirements:
         *
         * - the calling contract must have an ETH balance of at least `value`.
         * - the called Solidity function must be `payable`.
         *
         * _Available since v3.1._
         */
        function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
            return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
        }
        /**
         * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
         * with `errorMessage` as a fallback revert reason when `target` reverts.
         *
         * _Available since v3.1._
         */
        function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
            require(address(this).balance >= value, "Address: insufficient balance for call");
            require(isContract(target), "Address: call to non-contract");
            // solhint-disable-next-line avoid-low-level-calls
            (bool success, bytes memory returndata) = target.call{ value: value }(data);
            return _verifyCallResult(success, returndata, errorMessage);
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but performing a static call.
         *
         * _Available since v3.3._
         */
        function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
            return functionStaticCall(target, data, "Address: low-level static call failed");
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
         * but performing a static call.
         *
         * _Available since v3.3._
         */
        function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
            require(isContract(target), "Address: static call to non-contract");
            // solhint-disable-next-line avoid-low-level-calls
            (bool success, bytes memory returndata) = target.staticcall(data);
            return _verifyCallResult(success, returndata, errorMessage);
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but performing a delegate call.
         *
         * _Available since v3.4._
         */
        function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
            return functionDelegateCall(target, data, "Address: low-level delegate call failed");
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
         * but performing a delegate call.
         *
         * _Available since v3.4._
         */
        function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
            require(isContract(target), "Address: delegate call to non-contract");
            // solhint-disable-next-line avoid-low-level-calls
            (bool success, bytes memory returndata) = target.delegatecall(data);
            return _verifyCallResult(success, returndata, errorMessage);
        }
        function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
            if (success) {
                return returndata;
            } else {
                // Look for revert reason and bubble it up if present
                if (returndata.length > 0) {
                    // The easiest way to bubble the revert reason is using memory via assembly
                    // solhint-disable-next-line no-inline-assembly
                    assembly {
                        let returndata_size := mload(returndata)
                        revert(add(32, returndata), returndata_size)
                    }
                } else {
                    revert(errorMessage);
                }
            }
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.6.12;
    pragma experimental ABIEncoderV2;
    import "./Accounting.sol";
    import "../libraries/Lib_MerkleTree.sol";
    /**
     * @dev Bridge extends the accounting system and encapsulates the logic that is shared by both the
     * L1 and L2 Bridges. It allows to TransferRoots to be set by parent contracts and for those
     * TransferRoots to be withdrawn against. It also allows the bonder to bond and withdraw Transfers
     * directly through `bondWithdrawal` and then settle those bonds against their TransferRoot once it
     * has been set.
     */
    abstract contract Bridge is Accounting {
        using Lib_MerkleTree for bytes32;
        struct TransferRoot {
            uint256 total;
            uint256 amountWithdrawn;
            uint256 createdAt;
        }
        /* ========== Events ========== */
        event Withdrew(
            bytes32 indexed transferId,
            address indexed recipient,
            uint256 amount,
            bytes32 transferNonce
        );
        event WithdrawalBonded(
            bytes32 indexed transferId,
            uint256 amount
        );
        event WithdrawalBondSettled(
            address indexed bonder,
            bytes32 indexed transferId,
            bytes32 indexed rootHash
        );
        event MultipleWithdrawalsSettled(
            address indexed bonder,
            bytes32 indexed rootHash,
            uint256 totalBondsSettled
        );
        event TransferRootSet(
            bytes32 indexed rootHash,
            uint256 totalAmount
        );
        /* ========== State ========== */
        mapping(bytes32 => TransferRoot) private _transferRoots;
        mapping(bytes32 => bool) private _spentTransferIds;
        mapping(address => mapping(bytes32 => uint256)) private _bondedWithdrawalAmounts;
        uint256 constant RESCUE_DELAY = 8 weeks;
        constructor(address[] memory bonders) public Accounting(bonders) {}
        /* ========== Public Getters ========== */
        /**
         * @dev Get the hash that represents an individual Transfer.
         * @param chainId The id of the destination chain
         * @param recipient The address receiving the Transfer
         * @param amount The amount being transferred including the `_bonderFee`
         * @param transferNonce Used to avoid transferId collisions
         * @param bonderFee The amount paid to the address that withdraws the Transfer
         * @param amountOutMin The minimum amount received after attempting to swap in the destination
         * AMM market. 0 if no swap is intended.
         * @param deadline The deadline for swapping in the destination AMM market. 0 if no
         * swap is intended.
         */
        function getTransferId(
            uint256 chainId,
            address recipient,
            uint256 amount,
            bytes32 transferNonce,
            uint256 bonderFee,
            uint256 amountOutMin,
            uint256 deadline
        )
            public
            pure
            returns (bytes32)
        {
            return keccak256(abi.encode(
                chainId,
                recipient,
                amount,
                transferNonce,
                bonderFee,
                amountOutMin,
                deadline
            ));
        }
        /**
         * @notice getChainId can be overridden by subclasses if needed for compatibility or testing purposes.
         * @dev Get the current chainId
         * @return chainId The current chainId
         */
        function getChainId() public virtual view returns (uint256 chainId) {
            this; // Silence state mutability warning without generating any additional byte code
            assembly {
                chainId := chainid()
            }
        }
        /**
         * @dev Get the TransferRoot id for a given rootHash and totalAmount
         * @param rootHash The Merkle root of the TransferRoot
         * @param totalAmount The total of all Transfers in the TransferRoot
         * @return The calculated transferRootId
         */
        function getTransferRootId(bytes32 rootHash, uint256 totalAmount) public pure returns (bytes32) {
            return keccak256(abi.encodePacked(rootHash, totalAmount));
        }
        /**
         * @dev Get the TransferRoot for a given rootHash and totalAmount
         * @param rootHash The Merkle root of the TransferRoot
         * @param totalAmount The total of all Transfers in the TransferRoot
         * @return The TransferRoot with the calculated transferRootId
         */
        function getTransferRoot(bytes32 rootHash, uint256 totalAmount) public view returns (TransferRoot memory) {
            return _transferRoots[getTransferRootId(rootHash, totalAmount)];
        }
        /**
         * @dev Get the amount bonded for the withdrawal of a transfer
         * @param bonder The Bonder of the withdrawal
         * @param transferId The Transfer's unique identifier
         * @return The amount bonded for a Transfer withdrawal
         */
        function getBondedWithdrawalAmount(address bonder, bytes32 transferId) external view returns (uint256) {
            return _bondedWithdrawalAmounts[bonder][transferId];
        }
        /**
         * @dev Get the spent status of a transfer ID
         * @param transferId The transfer's unique identifier
         * @return True if the transferId has been spent
         */
        function isTransferIdSpent(bytes32 transferId) external view returns (bool) {
            return _spentTransferIds[transferId];
        }
        /* ========== User/Relayer External Functions ========== */
        /**
         * @notice Can be called by anyone (recipient or relayer)
         * @dev Withdraw a Transfer from its destination bridge
         * @param recipient The address receiving the Transfer
         * @param amount The amount being transferred including the `_bonderFee`
         * @param transferNonce Used to avoid transferId collisions
         * @param bonderFee The amount paid to the address that withdraws the Transfer
         * @param amountOutMin The minimum amount received after attempting to swap in the destination
         * AMM market. 0 if no swap is intended. (only used to calculate `transferId` in this function)
         * @param deadline The deadline for swapping in the destination AMM market. 0 if no
         * swap is intended. (only used to calculate `transferId` in this function)
         * @param rootHash The Merkle root of the TransferRoot
         * @param transferRootTotalAmount The total amount being transferred in a TransferRoot
         * @param transferIdTreeIndex The index of the transferId in the Merkle tree
         * @param siblings The siblings of the transferId in the Merkle tree
         * @param totalLeaves The total number of leaves in the Merkle tree
         */
        function withdraw(
            address recipient,
            uint256 amount,
            bytes32 transferNonce,
            uint256 bonderFee,
            uint256 amountOutMin,
            uint256 deadline,
            bytes32 rootHash,
            uint256 transferRootTotalAmount,
            uint256 transferIdTreeIndex,
            bytes32[] calldata siblings,
            uint256 totalLeaves
        )
            external
            nonReentrant
        {
            bytes32 transferId = getTransferId(
                getChainId(),
                recipient,
                amount,
                transferNonce,
                bonderFee,
                amountOutMin,
                deadline
            );
            require(
                rootHash.verify(
                    transferId,
                    transferIdTreeIndex,
                    siblings,
                    totalLeaves
                )
            , "BRG: Invalid transfer proof");
            bytes32 transferRootId = getTransferRootId(rootHash, transferRootTotalAmount);
            _addToAmountWithdrawn(transferRootId, amount);
            _fulfillWithdraw(transferId, recipient, amount, uint256(0));
            emit Withdrew(transferId, recipient, amount, transferNonce);
        }
        /**
         * @dev Allows the bonder to bond individual withdrawals before their TransferRoot has been committed.
         * @param recipient The address receiving the Transfer
         * @param amount The amount being transferred including the `_bonderFee`
         * @param transferNonce Used to avoid transferId collisions
         * @param bonderFee The amount paid to the address that withdraws the Transfer
         */
        function bondWithdrawal(
            address recipient,
            uint256 amount,
            bytes32 transferNonce,
            uint256 bonderFee
        )
            external
            onlyBonder
            requirePositiveBalance
            nonReentrant
        {
            bytes32 transferId = getTransferId(
                getChainId(),
                recipient,
                amount,
                transferNonce,
                bonderFee,
                0,
                0
            );
            _bondWithdrawal(transferId, amount);
            _fulfillWithdraw(transferId, recipient, amount, bonderFee);
        }
        /**
         * @dev Refunds the Bonder's stake from a bonded withdrawal and counts that withdrawal against
         * its TransferRoot.
         * @param bonder The Bonder of the withdrawal
         * @param transferId The Transfer's unique identifier
         * @param rootHash The Merkle root of the TransferRoot
         * @param transferRootTotalAmount The total amount being transferred in a TransferRoot
         * @param transferIdTreeIndex The index of the transferId in the Merkle tree
         * @param siblings The siblings of the transferId in the Merkle tree
         * @param totalLeaves The total number of leaves in the Merkle tree
         */
        function settleBondedWithdrawal(
            address bonder,
            bytes32 transferId,
            bytes32 rootHash,
            uint256 transferRootTotalAmount,
            uint256 transferIdTreeIndex,
            bytes32[] calldata siblings,
            uint256 totalLeaves
        )
            external
        {
            require(
                rootHash.verify(
                    transferId,
                    transferIdTreeIndex,
                    siblings,
                    totalLeaves
                )
            , "BRG: Invalid transfer proof");
            bytes32 transferRootId = getTransferRootId(rootHash, transferRootTotalAmount);
            uint256 amount = _bondedWithdrawalAmounts[bonder][transferId];
            require(amount > 0, "L2_BRG: transferId has no bond");
            _bondedWithdrawalAmounts[bonder][transferId] = 0;
            _addToAmountWithdrawn(transferRootId, amount);
            _addCredit(bonder, amount);
            emit WithdrawalBondSettled(bonder, transferId, rootHash);
        }
        /**
         * @dev Refunds the Bonder for all withdrawals that they bonded in a TransferRoot.
         * @param bonder The address of the Bonder being refunded
         * @param transferIds All transferIds in the TransferRoot in order
         * @param totalAmount The totalAmount of the TransferRoot
         */
        function settleBondedWithdrawals(
            address bonder,
            // transferIds _must_ be calldata or it will be mutated by Lib_MerkleTree.getMerkleRoot
            bytes32[] calldata transferIds,
            uint256 totalAmount
        )
            external
        {
            bytes32 rootHash = Lib_MerkleTree.getMerkleRoot(transferIds);
            bytes32 transferRootId = getTransferRootId(rootHash, totalAmount);
            uint256 totalBondsSettled = 0;
            for(uint256 i = 0; i < transferIds.length; i++) {
                uint256 transferBondAmount = _bondedWithdrawalAmounts[bonder][transferIds[i]];
                if (transferBondAmount > 0) {
                    totalBondsSettled = totalBondsSettled.add(transferBondAmount);
                    _bondedWithdrawalAmounts[bonder][transferIds[i]] = 0;
                }
            }
            _addToAmountWithdrawn(transferRootId, totalBondsSettled);
            _addCredit(bonder, totalBondsSettled);
            emit MultipleWithdrawalsSettled(bonder, rootHash, totalBondsSettled);
        }
        /* ========== External TransferRoot Rescue ========== */
        /**
         * @dev Allows governance to withdraw the remaining amount from a TransferRoot after the rescue delay has passed.
         * @param rootHash the Merkle root of the TransferRoot
         * @param originalAmount The TransferRoot's recorded total
         * @param recipient The address receiving the remaining balance
         */
        function rescueTransferRoot(bytes32 rootHash, uint256 originalAmount, address recipient) external onlyGovernance {
            bytes32 transferRootId = getTransferRootId(rootHash, originalAmount);
            TransferRoot memory transferRoot = getTransferRoot(rootHash, originalAmount);
            require(transferRoot.createdAt != 0, "BRG: TransferRoot not found");
            assert(transferRoot.total == originalAmount);
            uint256 rescueDelayEnd = transferRoot.createdAt.add(RESCUE_DELAY);
            require(block.timestamp >= rescueDelayEnd, "BRG: TransferRoot cannot be rescued before the Rescue Delay");
            uint256 remainingAmount = transferRoot.total.sub(transferRoot.amountWithdrawn);
            _addToAmountWithdrawn(transferRootId, remainingAmount);
            _transferFromBridge(recipient, remainingAmount);
        }
        /* ========== Internal Functions ========== */
        function _markTransferSpent(bytes32 transferId) internal {
            require(!_spentTransferIds[transferId], "BRG: The transfer has already been withdrawn");
            _spentTransferIds[transferId] = true;
        }
        function _addToAmountWithdrawn(bytes32 transferRootId, uint256 amount) internal {
            TransferRoot storage transferRoot = _transferRoots[transferRootId];
            require(transferRoot.total > 0, "BRG: Transfer root not found");
            uint256 newAmountWithdrawn = transferRoot.amountWithdrawn.add(amount);
            require(newAmountWithdrawn <= transferRoot.total, "BRG: Withdrawal exceeds TransferRoot total");
            transferRoot.amountWithdrawn = newAmountWithdrawn;
        }
        function _setTransferRoot(bytes32 rootHash, uint256 totalAmount) internal {
            bytes32 transferRootId = getTransferRootId(rootHash, totalAmount);
            require(_transferRoots[transferRootId].total == 0, "BRG: Transfer root already set");
            require(totalAmount > 0, "BRG: Cannot set TransferRoot totalAmount of 0");
            _transferRoots[transferRootId] = TransferRoot(totalAmount, 0, block.timestamp);
            emit TransferRootSet(rootHash, totalAmount);
        }
        function _bondWithdrawal(bytes32 transferId, uint256 amount) internal {
            require(_bondedWithdrawalAmounts[msg.sender][transferId] == 0, "BRG: Withdrawal has already been bonded");
            _addDebit(msg.sender, amount);
            _bondedWithdrawalAmounts[msg.sender][transferId] = amount;
            emit WithdrawalBonded(transferId, amount);
        }
        /* ========== Private Functions ========== */
        /// @dev Completes the Transfer, distributes the Bonder fee and marks the Transfer as spent.
        function _fulfillWithdraw(
            bytes32 transferId,
            address recipient,
            uint256 amount,
            uint256 bonderFee
        ) private {
            _markTransferSpent(transferId);
            _transferFromBridge(recipient, amount.sub(bonderFee));
            if (bonderFee > 0) {
                _transferFromBridge(msg.sender, bonderFee);
            }
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity >=0.6.12 <0.8.0;
    pragma experimental ABIEncoderV2;
    interface IMessengerWrapper {
        function sendCrossDomainMessage(bytes memory _calldata) external;
        function verifySender(address l1BridgeCaller, bytes memory _data) external;
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.6.12;
    pragma experimental ABIEncoderV2;
    import "@openzeppelin/contracts/math/SafeMath.sol";
    import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
    /**
     * @dev Accounting is an abstract contract that encapsulates the most critical logic in the Hop contracts.
     * The accounting system works by using two balances that can only increase `_credit` and `_debit`.
     * A bonder's available balance is the total credit minus the total debit. The contract exposes
     * two external functions that allows a bonder to stake and unstake and exposes two internal
     * functions to its child contracts that allow the child contract to add to the credit 
     * and debit balance. In addition, child contracts can override `_additionalDebit` to account
     * for any additional debit balance in an alternative way. Lastly, it exposes a modifier,
     * `requirePositiveBalance`, that can be used by child contracts to ensure the bonder does not
     * use more than its available stake.
     */
    abstract contract Accounting is ReentrancyGuard {
        using SafeMath for uint256;
        mapping(address => bool) private _isBonder;
        mapping(address => uint256) private _credit;
        mapping(address => uint256) private _debit;
        event Stake (
            address indexed account,
            uint256 amount
        );
        event Unstake (
            address indexed account,
            uint256 amount
        );
        event BonderAdded (
            address indexed newBonder
        );
        event BonderRemoved (
            address indexed previousBonder
        );
        /* ========== Modifiers ========== */
        modifier onlyBonder {
            require(_isBonder[msg.sender], "ACT: Caller is not bonder");
            _;
        }
        modifier onlyGovernance {
            _requireIsGovernance();
            _;
        }
        /// @dev Used by parent contract to ensure that the Bonder is solvent at the end of the transaction.
        modifier requirePositiveBalance {
            _;
            require(getCredit(msg.sender) >= getDebitAndAdditionalDebit(msg.sender), "ACT: Not enough available credit");
        }
        /// @dev Sets the Bonder addresses
        constructor(address[] memory bonders) public {
            for (uint256 i = 0; i < bonders.length; i++) {
                require(_isBonder[bonders[i]] == false, "ACT: Cannot add duplicate bonder");
                _isBonder[bonders[i]] = true;
                emit BonderAdded(bonders[i]);
            }
        }
        /* ========== Virtual functions ========== */
        /**
         * @dev The following functions are overridden in L1_Bridge and L2_Bridge
         */
        function _transferFromBridge(address recipient, uint256 amount) internal virtual;
        function _transferToBridge(address from, uint256 amount) internal virtual;
        function _requireIsGovernance() internal virtual;
        /**
         * @dev This function can be optionally overridden by a parent contract to track any additional
         * debit balance in an alternative way.
         */
        function _additionalDebit(address /*bonder*/) internal view virtual returns (uint256) {
            this; // Silence state mutability warning without generating any additional byte code
            return 0;
        }
        /* ========== Public/external getters ========== */
        /**
         * @dev Check if address is a Bonder
         * @param maybeBonder The address being checked
         * @return true if address is a Bonder
         */
        function getIsBonder(address maybeBonder) public view returns (bool) {
            return _isBonder[maybeBonder];
        }
        /**
         * @dev Get the Bonder's credit balance
         * @param bonder The owner of the credit balance being checked
         * @return The credit balance for the Bonder
         */
        function getCredit(address bonder) public view returns (uint256) {
            return _credit[bonder];
        }
        /**
         * @dev Gets the debit balance tracked by `_debit` and does not include `_additionalDebit()`
         * @param bonder The owner of the debit balance being checked
         * @return The debit amount for the Bonder
         */
        function getRawDebit(address bonder) external view returns (uint256) {
            return _debit[bonder];
        }
        /**
         * @dev Get the Bonder's total debit
         * @param bonder The owner of the debit balance being checked
         * @return The Bonder's total debit balance
         */
        function getDebitAndAdditionalDebit(address bonder) public view returns (uint256) {
            return _debit[bonder].add(_additionalDebit(bonder));
        }
        /* ========== Bonder external functions ========== */
        /** 
         * @dev Allows the Bonder to deposit tokens and increase its credit balance
         * @param bonder The address being staked on
         * @param amount The amount being staked
         */
        function stake(address bonder, uint256 amount) external payable nonReentrant {
            require(_isBonder[bonder] == true, "ACT: Address is not bonder");
            _transferToBridge(msg.sender, amount);
            _addCredit(bonder, amount);
            emit Stake(bonder, amount);
        }
        /**
         * @dev Allows the caller to withdraw any available balance and add to their debit balance
         * @param amount The amount being unstaked
         */
        function unstake(uint256 amount) external requirePositiveBalance nonReentrant {
            _addDebit(msg.sender, amount);
            _transferFromBridge(msg.sender, amount);
            emit Unstake(msg.sender, amount);
        }
        /**
         * @dev Add Bonder to allowlist
         * @param bonder The address being added as a Bonder
         */
        function addBonder(address bonder) external onlyGovernance {
            require(_isBonder[bonder] == false, "ACT: Address is already bonder");
            _isBonder[bonder] = true;
            emit BonderAdded(bonder);
        }
        /**
         * @dev Remove Bonder from allowlist
         * @param bonder The address being removed as a Bonder
         */
        function removeBonder(address bonder) external onlyGovernance {
            require(_isBonder[bonder] == true, "ACT: Address is not bonder");
            _isBonder[bonder] = false;
            emit BonderRemoved(bonder);
        }
        /* ========== Internal functions ========== */
        function _addCredit(address bonder, uint256 amount) internal {
            _credit[bonder] = _credit[bonder].add(amount);
        }
        function _addDebit(address bonder, uint256 amount) internal {
            _debit[bonder] = _debit[bonder].add(amount);
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity >0.5.0 <0.8.0;
    /**
     * @title Lib_MerkleTree
     * @author River Keefer
     */
    library Lib_MerkleTree {
        /**********************
         * Internal Functions *
         **********************/
        /**
         * Calculates a merkle root for a list of 32-byte leaf hashes.  WARNING: If the number
         * of leaves passed in is not a power of two, it pads out the tree with zero hashes.
         * If you do not know the original length of elements for the tree you are verifying,
         * then this may allow empty leaves past _elements.length to pass a verification check down the line.
         * Note that the _elements argument is modified, therefore it must not be used again afterwards
         * @param _elements Array of hashes from which to generate a merkle root.
         * @return Merkle root of the leaves, with zero hashes for non-powers-of-two (see above).
         */
        function getMerkleRoot(
            bytes32[] memory _elements
        )
            internal
            pure
            returns (
                bytes32
            )
        {
            require(
                _elements.length > 0,
                "Lib_MerkleTree: Must provide at least one leaf hash."
            );
            if (_elements.length == 1) {
                return _elements[0];
            }
            uint256[16] memory defaults = [
                0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563,
                0x633dc4d7da7256660a892f8f1604a44b5432649cc8ec5cb3ced4c4e6ac94dd1d,
                0x890740a8eb06ce9be422cb8da5cdafc2b58c0a5e24036c578de2a433c828ff7d,
                0x3b8ec09e026fdc305365dfc94e189a81b38c7597b3d941c279f042e8206e0bd8,
                0xecd50eee38e386bd62be9bedb990706951b65fe053bd9d8a521af753d139e2da,
                0xdefff6d330bb5403f63b14f33b578274160de3a50df4efecf0e0db73bcdd3da5,
                0x617bdd11f7c0a11f49db22f629387a12da7596f9d1704d7465177c63d88ec7d7,
                0x292c23a9aa1d8bea7e2435e555a4a60e379a5a35f3f452bae60121073fb6eead,
                0xe1cea92ed99acdcb045a6726b2f87107e8a61620a232cf4d7d5b5766b3952e10,
                0x7ad66c0a68c72cb89e4fb4303841966e4062a76ab97451e3b9fb526a5ceb7f82,
                0xe026cc5a4aed3c22a58cbd3d2ac754c9352c5436f638042dca99034e83636516,
                0x3d04cffd8b46a874edf5cfae63077de85f849a660426697b06a829c70dd1409c,
                0xad676aa337a485e4728a0b240d92b3ef7b3c372d06d189322bfd5f61f1e7203e,
                0xa2fca4a49658f9fab7aa63289c91b7c7b6c832a6d0e69334ff5b0a3483d09dab,
                0x4ebfd9cd7bca2505f7bef59cc1c12ecc708fff26ae4af19abe852afe9e20c862,
                0x2def10d13dd169f550f578bda343d9717a138562e0093b380a1120789d53cf10
            ];
            // Reserve memory space for our hashes.
            bytes memory buf = new bytes(64);
            // We'll need to keep track of left and right siblings.
            bytes32 leftSibling;
            bytes32 rightSibling;
            // Number of non-empty nodes at the current depth.
            uint256 rowSize = _elements.length;
            // Current depth, counting from 0 at the leaves
            uint256 depth = 0;
            // Common sub-expressions
            uint256 halfRowSize;         // rowSize / 2
            bool rowSizeIsOdd;           // rowSize % 2 == 1
            while (rowSize > 1) {
                halfRowSize = rowSize / 2;
                rowSizeIsOdd = rowSize % 2 == 1;
                for (uint256 i = 0; i < halfRowSize; i++) {
                    leftSibling  = _elements[(2 * i)    ];
                    rightSibling = _elements[(2 * i) + 1];
                    assembly {
                        mstore(add(buf, 32), leftSibling )
                        mstore(add(buf, 64), rightSibling)
                    }
                    _elements[i] = keccak256(buf);
                }
                if (rowSizeIsOdd) {
                    leftSibling  = _elements[rowSize - 1];
                    rightSibling = bytes32(defaults[depth]);
                    assembly {
                        mstore(add(buf, 32), leftSibling)
                        mstore(add(buf, 64), rightSibling)
                    }
                    _elements[halfRowSize] = keccak256(buf);
                }
                rowSize = halfRowSize + (rowSizeIsOdd ? 1 : 0);
                depth++;
            }
            return _elements[0];
        }
        /**
         * Verifies a merkle branch for the given leaf hash.  Assumes the original length
         * of leaves generated is a known, correct input, and does not return true for indices
         * extending past that index (even if _siblings would be otherwise valid.)
         * @param _root The Merkle root to verify against.
         * @param _leaf The leaf hash to verify inclusion of.
         * @param _index The index in the tree of this leaf.
         * @param _siblings Array of sibline nodes in the inclusion proof, starting from depth 0 (bottom of the tree).
         * @param _totalLeaves The total number of leaves originally passed into.
         * @return Whether or not the merkle branch and leaf passes verification.
         */
        function verify(
            bytes32 _root,
            bytes32 _leaf,
            uint256 _index,
            bytes32[] memory _siblings,
            uint256 _totalLeaves
        )
            internal
            pure
            returns (
                bool
            )
        {
            require(
                _totalLeaves > 0,
                "Lib_MerkleTree: Total leaves must be greater than zero."
            );
            require(
                _index < _totalLeaves,
                "Lib_MerkleTree: Index out of bounds."
            );
            require(
                _siblings.length == _ceilLog2(_totalLeaves),
                "Lib_MerkleTree: Total siblings does not correctly correspond to total leaves."
            );
            bytes32 computedRoot = _leaf;
            for (uint256 i = 0; i < _siblings.length; i++) {
                if ((_index & 1) == 1) {
                    computedRoot = keccak256(
                        abi.encodePacked(
                            _siblings[i],
                            computedRoot
                        )
                    );
                } else {
                    computedRoot = keccak256(
                        abi.encodePacked(
                            computedRoot,
                            _siblings[i]
                        )
                    );
                }
                _index >>= 1;
            }
            return _root == computedRoot;
        }
        /*********************
         * Private Functions *
         *********************/
        /**
         * Calculates the integer ceiling of the log base 2 of an input.
         * @param _in Unsigned input to calculate the log.
         * @return ceil(log_base_2(_in))
         */
        function _ceilLog2(
            uint256 _in
        )
            private
            pure
            returns (
                uint256
            )
        {
            require(
                _in > 0,
                "Lib_MerkleTree: Cannot compute ceil(log_2) of 0."
            );
            if (_in == 1) {
                return 0;
            }
            // Find the highest set bit (will be floor(log_2)).
            // Borrowed with <3 from https://github.com/ethereum/solidity-examples
            uint256 val = _in;
            uint256 highest = 0;
            for (uint256 i = 128; i >= 1; i >>= 1) {
                if (val & (uint(1) << i) - 1 << i != 0) {
                    highest += i;
                    val >>= i;
                }
            }
            // Increment by one if this is not a perfect logarithm.
            if ((uint(1) << highest) != _in) {
                highest += 1;
            }
            return highest;
        }
    }// SPDX-License-Identifier: MIT
    pragma solidity >=0.6.0 <0.8.0;
    /**
     * @dev Contract module that helps prevent reentrant calls to a function.
     *
     * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
     * available, which can be applied to functions to make sure there are no nested
     * (reentrant) calls to them.
     *
     * Note that because there is a single `nonReentrant` guard, functions marked as
     * `nonReentrant` may not call one another. This can be worked around by making
     * those functions `private`, and then adding `external` `nonReentrant` entry
     * points to them.
     *
     * TIP: If you would like to learn more about reentrancy and alternative ways
     * to protect against it, check out our blog post
     * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
     */
    abstract contract ReentrancyGuard {
        // Booleans are more expensive than uint256 or any type that takes up a full
        // word because each write operation emits an extra SLOAD to first read the
        // slot's contents, replace the bits taken up by the boolean, and then write
        // back. This is the compiler's defense against contract upgrades and
        // pointer aliasing, and it cannot be disabled.
        // The values being non-zero value makes deployment a bit more expensive,
        // but in exchange the refund on every call to nonReentrant will be lower in
        // amount. Since refunds are capped to a percentage of the total
        // transaction's gas, it is best to keep them low in cases like this one, to
        // increase the likelihood of the full refund coming into effect.
        uint256 private constant _NOT_ENTERED = 1;
        uint256 private constant _ENTERED = 2;
        uint256 private _status;
        constructor () internal {
            _status = _NOT_ENTERED;
        }
        /**
         * @dev Prevents a contract from calling itself, directly or indirectly.
         * Calling a `nonReentrant` function from another `nonReentrant`
         * function is not supported. It is possible to prevent this from happening
         * by making the `nonReentrant` function external, and make it call a
         * `private` function that does the actual work.
         */
        modifier nonReentrant() {
            // On the first call to nonReentrant, _notEntered will be true
            require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
            // Any calls to nonReentrant after this point will fail
            _status = _ENTERED;
            _;
            // By storing the original value once again, a refund is triggered (see
            // https://eips.ethereum.org/EIPS/eip-2200)
            _status = _NOT_ENTERED;
        }
    }
    

    File 2 of 2: MaticToken
    pragma solidity 0.5.2;
    
    // File: openzeppelin-solidity/contracts/token/ERC20/IERC20.sol
    
    /**
     * @title ERC20 interface
     * @dev see https://github.com/ethereum/EIPs/issues/20
     */
    interface IERC20 {
        function transfer(address to, uint256 value) external returns (bool);
    
        function approve(address spender, uint256 value) external returns (bool);
    
        function transferFrom(address from, address to, uint256 value) external returns (bool);
    
        function totalSupply() external view returns (uint256);
    
        function balanceOf(address who) external view returns (uint256);
    
        function allowance(address owner, address spender) external view returns (uint256);
    
        event Transfer(address indexed from, address indexed to, uint256 value);
    
        event Approval(address indexed owner, address indexed spender, uint256 value);
    }
    
    // File: openzeppelin-solidity/contracts/math/SafeMath.sol
    
    /**
     * @title SafeMath
     * @dev Unsigned math operations with safety checks that revert on error
     */
    library SafeMath {
        /**
        * @dev Multiplies two unsigned integers, reverts on 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-solidity/pull/522
            if (a == 0) {
                return 0;
            }
    
            uint256 c = a * b;
            require(c / a == b);
    
            return c;
        }
    
        /**
        * @dev Integer division of two unsigned integers truncating the quotient, reverts on division by zero.
        */
        function div(uint256 a, uint256 b) internal pure returns (uint256) {
            // Solidity only automatically asserts when dividing by 0
            require(b > 0);
            uint256 c = a / b;
            // assert(a == b * c + a % b); // There is no case in which this doesn't hold
    
            return c;
        }
    
        /**
        * @dev Subtracts two unsigned integers, reverts on overflow (i.e. if subtrahend is greater than minuend).
        */
        function sub(uint256 a, uint256 b) internal pure returns (uint256) {
            require(b <= a);
            uint256 c = a - b;
    
            return c;
        }
    
        /**
        * @dev Adds two unsigned integers, reverts on overflow.
        */
        function add(uint256 a, uint256 b) internal pure returns (uint256) {
            uint256 c = a + b;
            require(c >= a);
    
            return c;
        }
    
        /**
        * @dev Divides two unsigned integers and returns the remainder (unsigned integer modulo),
        * reverts when dividing by zero.
        */
        function mod(uint256 a, uint256 b) internal pure returns (uint256) {
            require(b != 0);
            return a % b;
        }
    }
    
    // File: openzeppelin-solidity/contracts/token/ERC20/ERC20.sol
    
    /**
     * @title Standard ERC20 token
     *
     * @dev Implementation of the basic standard token.
     * https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md
     * Originally based on code by FirstBlood:
     * https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol
     *
     * This implementation emits additional Approval events, allowing applications to reconstruct the allowance status for
     * all accounts just by listening to said events. Note that this isn't required by the specification, and other
     * compliant implementations may not do it.
     */
    contract ERC20 is IERC20 {
        using SafeMath for uint256;
    
        mapping (address => uint256) private _balances;
    
        mapping (address => mapping (address => uint256)) private _allowed;
    
        uint256 private _totalSupply;
    
        /**
        * @dev Total number of tokens in existence
        */
        function totalSupply() public view returns (uint256) {
            return _totalSupply;
        }
    
        /**
        * @dev Gets the balance of the specified address.
        * @param owner The address to query the balance of.
        * @return An uint256 representing the amount owned by the passed address.
        */
        function balanceOf(address owner) public view returns (uint256) {
            return _balances[owner];
        }
    
        /**
         * @dev Function to check the amount of tokens that an owner allowed to a spender.
         * @param owner address The address which owns the funds.
         * @param spender address The address which will spend the funds.
         * @return A uint256 specifying the amount of tokens still available for the spender.
         */
        function allowance(address owner, address spender) public view returns (uint256) {
            return _allowed[owner][spender];
        }
    
        /**
        * @dev Transfer token for a specified address
        * @param to The address to transfer to.
        * @param value The amount to be transferred.
        */
        function transfer(address to, uint256 value) public returns (bool) {
            _transfer(msg.sender, to, value);
            return true;
        }
    
        /**
         * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
         * 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
         * @param spender The address which will spend the funds.
         * @param value The amount of tokens to be spent.
         */
        function approve(address spender, uint256 value) public returns (bool) {
            require(spender != address(0));
    
            _allowed[msg.sender][spender] = value;
            emit Approval(msg.sender, spender, value);
            return true;
        }
    
        /**
         * @dev Transfer tokens from one address to another.
         * Note that while this function emits an Approval event, this is not required as per the specification,
         * and other compliant implementations may not emit the event.
         * @param from address The address which you want to send tokens from
         * @param to address The address which you want to transfer to
         * @param value uint256 the amount of tokens to be transferred
         */
        function transferFrom(address from, address to, uint256 value) public returns (bool) {
            _allowed[from][msg.sender] = _allowed[from][msg.sender].sub(value);
            _transfer(from, to, value);
            emit Approval(from, msg.sender, _allowed[from][msg.sender]);
            return true;
        }
    
        /**
         * @dev Increase the amount of tokens that an owner allowed to a spender.
         * approve should be called when allowed_[_spender] == 0. To increment
         * allowed value is better to use this function to avoid 2 calls (and wait until
         * the first transaction is mined)
         * From MonolithDAO Token.sol
         * Emits an Approval event.
         * @param spender The address which will spend the funds.
         * @param addedValue The amount of tokens to increase the allowance by.
         */
        function increaseAllowance(address spender, uint256 addedValue) public returns (bool) {
            require(spender != address(0));
    
            _allowed[msg.sender][spender] = _allowed[msg.sender][spender].add(addedValue);
            emit Approval(msg.sender, spender, _allowed[msg.sender][spender]);
            return true;
        }
    
        /**
         * @dev Decrease the amount of tokens that an owner allowed to a spender.
         * approve should be called when allowed_[_spender] == 0. To decrement
         * allowed value is better to use this function to avoid 2 calls (and wait until
         * the first transaction is mined)
         * From MonolithDAO Token.sol
         * Emits an Approval event.
         * @param spender The address which will spend the funds.
         * @param subtractedValue The amount of tokens to decrease the allowance by.
         */
        function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) {
            require(spender != address(0));
    
            _allowed[msg.sender][spender] = _allowed[msg.sender][spender].sub(subtractedValue);
            emit Approval(msg.sender, spender, _allowed[msg.sender][spender]);
            return true;
        }
    
        /**
        * @dev Transfer token for a specified addresses
        * @param from The address to transfer from.
        * @param to The address to transfer to.
        * @param value The amount to be transferred.
        */
        function _transfer(address from, address to, uint256 value) internal {
            require(to != address(0));
    
            _balances[from] = _balances[from].sub(value);
            _balances[to] = _balances[to].add(value);
            emit Transfer(from, to, value);
        }
    
        /**
         * @dev Internal function that mints an amount of the token and assigns it to
         * an account. This encapsulates the modification of balances such that the
         * proper events are emitted.
         * @param account The account that will receive the created tokens.
         * @param value The amount that will be created.
         */
        function _mint(address account, uint256 value) internal {
            require(account != address(0));
    
            _totalSupply = _totalSupply.add(value);
            _balances[account] = _balances[account].add(value);
            emit Transfer(address(0), account, value);
        }
    
        /**
         * @dev Internal function that burns an amount of the token of a given
         * account.
         * @param account The account whose tokens will be burnt.
         * @param value The amount that will be burnt.
         */
        function _burn(address account, uint256 value) internal {
            require(account != address(0));
    
            _totalSupply = _totalSupply.sub(value);
            _balances[account] = _balances[account].sub(value);
            emit Transfer(account, address(0), value);
        }
    
        /**
         * @dev Internal function that burns an amount of the token of a given
         * account, deducting from the sender's allowance for said account. Uses the
         * internal burn function.
         * Emits an Approval event (reflecting the reduced allowance).
         * @param account The account whose tokens will be burnt.
         * @param value The amount that will be burnt.
         */
        function _burnFrom(address account, uint256 value) internal {
            _allowed[account][msg.sender] = _allowed[account][msg.sender].sub(value);
            _burn(account, value);
            emit Approval(account, msg.sender, _allowed[account][msg.sender]);
        }
    }
    
    // File: openzeppelin-solidity/contracts/access/Roles.sol
    
    /**
     * @title Roles
     * @dev Library for managing addresses assigned to a Role.
     */
    library Roles {
        struct Role {
            mapping (address => bool) bearer;
        }
    
        /**
         * @dev give an account access to this role
         */
        function add(Role storage role, address account) internal {
            require(account != address(0));
            require(!has(role, account));
    
            role.bearer[account] = true;
        }
    
        /**
         * @dev remove an account's access to this role
         */
        function remove(Role storage role, address account) internal {
            require(account != address(0));
            require(has(role, account));
    
            role.bearer[account] = false;
        }
    
        /**
         * @dev check if an account has this role
         * @return bool
         */
        function has(Role storage role, address account) internal view returns (bool) {
            require(account != address(0));
            return role.bearer[account];
        }
    }
    
    // File: openzeppelin-solidity/contracts/access/roles/PauserRole.sol
    
    contract PauserRole {
        using Roles for Roles.Role;
    
        event PauserAdded(address indexed account);
        event PauserRemoved(address indexed account);
    
        Roles.Role private _pausers;
    
        constructor () internal {
            _addPauser(msg.sender);
        }
    
        modifier onlyPauser() {
            require(isPauser(msg.sender));
            _;
        }
    
        function isPauser(address account) public view returns (bool) {
            return _pausers.has(account);
        }
    
        function addPauser(address account) public onlyPauser {
            _addPauser(account);
        }
    
        function renouncePauser() public {
            _removePauser(msg.sender);
        }
    
        function _addPauser(address account) internal {
            _pausers.add(account);
            emit PauserAdded(account);
        }
    
        function _removePauser(address account) internal {
            _pausers.remove(account);
            emit PauserRemoved(account);
        }
    }
    
    // File: openzeppelin-solidity/contracts/lifecycle/Pausable.sol
    
    /**
     * @title Pausable
     * @dev Base contract which allows children to implement an emergency stop mechanism.
     */
    contract Pausable is PauserRole {
        event Paused(address account);
        event Unpaused(address account);
    
        bool private _paused;
    
        constructor () internal {
            _paused = false;
        }
    
        /**
         * @return true if the contract is paused, false otherwise.
         */
        function paused() public view returns (bool) {
            return _paused;
        }
    
        /**
         * @dev Modifier to make a function callable only when the contract is not paused.
         */
        modifier whenNotPaused() {
            require(!_paused);
            _;
        }
    
        /**
         * @dev Modifier to make a function callable only when the contract is paused.
         */
        modifier whenPaused() {
            require(_paused);
            _;
        }
    
        /**
         * @dev called by the owner to pause, triggers stopped state
         */
        function pause() public onlyPauser whenNotPaused {
            _paused = true;
            emit Paused(msg.sender);
        }
    
        /**
         * @dev called by the owner to unpause, returns to normal state
         */
        function unpause() public onlyPauser whenPaused {
            _paused = false;
            emit Unpaused(msg.sender);
        }
    }
    
    // File: openzeppelin-solidity/contracts/token/ERC20/ERC20Pausable.sol
    
    /**
     * @title Pausable token
     * @dev ERC20 modified with pausable transfers.
     **/
    contract ERC20Pausable is ERC20, Pausable {
        function transfer(address to, uint256 value) public whenNotPaused returns (bool) {
            return super.transfer(to, value);
        }
    
        function transferFrom(address from, address to, uint256 value) public whenNotPaused returns (bool) {
            return super.transferFrom(from, to, value);
        }
    
        function approve(address spender, uint256 value) public whenNotPaused returns (bool) {
            return super.approve(spender, value);
        }
    
        function increaseAllowance(address spender, uint addedValue) public whenNotPaused returns (bool success) {
            return super.increaseAllowance(spender, addedValue);
        }
    
        function decreaseAllowance(address spender, uint subtractedValue) public whenNotPaused returns (bool success) {
            return super.decreaseAllowance(spender, subtractedValue);
        }
    }
    
    // File: openzeppelin-solidity/contracts/token/ERC20/ERC20Detailed.sol
    
    /**
     * @title ERC20Detailed token
     * @dev The decimals are only for visualization purposes.
     * All the operations are done using the smallest and indivisible token unit,
     * just as on Ethereum all the operations are done in wei.
     */
    contract ERC20Detailed is IERC20 {
        string private _name;
        string private _symbol;
        uint8 private _decimals;
    
        constructor (string memory name, string memory symbol, uint8 decimals) public {
            _name = name;
            _symbol = symbol;
            _decimals = decimals;
        }
    
        /**
         * @return the name of the token.
         */
        function name() public view returns (string memory) {
            return _name;
        }
    
        /**
         * @return the symbol of the token.
         */
        function symbol() public view returns (string memory) {
            return _symbol;
        }
    
        /**
         * @return the number of decimals of the token.
         */
        function decimals() public view returns (uint8) {
            return _decimals;
        }
    }
    
    // File: contracts/MaticToken.sol
    
    contract MaticToken is ERC20Pausable, ERC20Detailed {
        constructor (string memory name, string memory symbol, uint8 decimals, uint256 totalSupply)
        public
        ERC20Detailed (name, symbol, decimals) {
            _mint(msg.sender, totalSupply);
        }
    }