ETH Price: $2,531.98 (-0.28%)

Transaction Decoder

Block:
18588109 at Nov-17-2023 12:45:47 AM +UTC
Transaction Fee:
0.00669751527591432 ETH $16.96
Gas Used:
185,328 Gas / 36.138712315 Gwei

Emitted Events:

87 NerveMultiSigWalletIII.TransferFunds( to=0xf8aebeb76a87b1fac30524b4024b4afb8cab4b01, amount=11500000000000000 )
88 NerveMultiSigWalletIII.TxWithdrawCompleted( txKey=dc585df0b1c146508e0009cdb7b3fffdeaad95c62beec89a01d050d4c3b05682 )

Account State Difference:

  Address   Before After State Difference Code
(beaverbuild)
7.627368268428856995 Eth7.629703541428006371 Eth0.002335272999149376
0xC707E085...d937d7E82
(NerveNetwork: Bank Node Multi-Signature)
47.131465993720053045 Eth47.119965993720053045 Eth0.0115
0xd87F2ad3...CA71B3B56
0.169229441461638173 Eth
Nonce: 1420
0.162531926185723853 Eth
Nonce: 1421
0.00669751527591432
0xf8aeBeb7...b8cab4B01 0.017490943048462699 Eth0.028990943048462699 Eth0.0115

Execution Trace

NerveMultiSigWalletIII.createOrSignWithdraw( txKey=dc585df0b1c146508e0009cdb7b3fffdeaad95c62beec89a01d050d4c3b05682, to=0xf8aeBeb76A87b1faC30524b4024B4aFb8cab4B01, amount=11500000000000000, isERC20=False, ERC20=0x0000000000000000000000000000000000000000, signatures=0x051D5F3C403646496864B7B016F59F9251A144A9FF359E3C2D6CA6DC93589166270F22A32EF15B568D72327D7AB5AE3073FA7F37E69F818766DB250E1FF166A11C8C5A240AAA7AF7450A8A1A74C01182284DCFBAB1FF6EB0F02E25B5ABB81BCA3C137C0C2C8C90DDDA8DAB6D66DABFF1A44E721CAE6A70713DF4A7B6C0613E89A81C9AB2317CBC7FEA7031CBC3590F09C92CF33F14C2F8907BA55FBBA3906370252074024BF712C2F0AC5BEE477A70EB6DE71D9FA8574F4C7FECD64FBECC0587940C1BA6644DAEB87E0F2B7F167BF4049FBF7046176CBA4516E7C8D196D57684AB752049B107BEF99E47F2389109B092D38AD9B6449CA2A192B2891FB94E1FF0669FFB1C704505DAA9696CEDD9015AE3EACB084E0D20348678C4DE1C303D263AAEE7359D6FC2E80F497E7A2D49AC735C46F88EAB1DA517CCFCBDC0F93D9334D92BC65ED11BA643AB6FC2F88D1118AA1B5D03F755E514DD2DA513C65D87E43C79E2166774275ED9CA22C9E29E45CC8D08DCD8B59FFDD9C6764F551F0BF6EA1F7C38947DB9C71C534F526D3BF70F6AA417D15DF32FBA4BE82EB4DDE45512C662E2D6822CF9B1983A7FA677563B65CDC7602E780AC99BD83BD58A4586EB3E4665646797388CCFF01C5B54EF9D71FD0DA386461C5F29A0628DCC4AE66F348E6F91FFBA5932A62EAD1E75BB2C6B71FFD66929D53B5657F51D337022A61B45CC6090478365AD5F288EED1B483ABBC4A8DAEDD33F5BE8F9FA4FF36D490DF102E9603B37041DDC092C1E42C904C6E1F0804DCDAFCF9CA45F49C6AB98CAC461CB96C068308D77E8897520B2AB1B6E92511AAB45F8ECC9A19192F34853C3E8E7E16BC2CFFD88EB918092C1D705CC1835B022D2DE543E4AA6DF46471BCFF55E620C2961EFA2E4B3FCC0AC873653AB1C )
  • Null: 0x000...001.18c903a0( )
  • Null: 0x000...001.18c903a0( )
  • Null: 0x000...001.18c903a0( )
  • Null: 0x000...001.18c903a0( )
  • Null: 0x000...001.18c903a0( )
  • Null: 0x000...001.18c903a0( )
  • Null: 0x000...001.18c903a0( )
  • Null: 0x000...001.18c903a0( )
  • Null: 0x000...001.18c903a0( )
  • Null: 0x000...001.18c903a0( )
  • ETH 0.0115 0xf8aebeb76a87b1fac30524b4024b4afb8cab4b01.CALL( )
    pragma solidity ^0.5.5;
    
    /**
     * @dev Interface of the ERC20 standard as defined in the EIP. Does not include
     * the optional functions; to access them see {ERC20Detailed}.
     */
    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);
    }
    
    /**
     * @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 ERC20;` 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, mapping(address => uint8) storage bugERC20s) internal {
            callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value), bugERC20s);
        }
    
        function safeTransferFrom(IERC20 token, address from, address to, uint256 value, mapping(address => uint8) storage bugERC20s) internal {
            callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value), bugERC20s);
        }
    
    
    
    
        /**
         * @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, mapping(address => uint8) storage bugERC20s) 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.
    
            // A Solidity high level call has three parts:
            //  1. The target address is checked to verify it contains contract code
            //  2. The call itself is made, and success asserted
            //  3. The return value is decoded, which in turn checks the size of the returned data.
            // solhint-disable-next-line max-line-length
            require(address(token).isContract(), "SafeERC20: call to non-contract");
    
            // solhint-disable-next-line avoid-low-level-calls
            (bool success, bytes memory returndata) = address(token).call(data);
            require(success, "SafeERC20: low-level call failed");
    
            if (bugERC20s[address(token)] != 0) {
                return;
            }
            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");
            }
        }
    }
    
    // Part: ReentrancyGuard
    
    /**
     * @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].
     */
    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;
        }
    }
    
    /**
     * @dev Wrappers over Solidity's arithmetic operations with added overflow
     * checks.
     *
     * Arithmetic operations in Solidity wrap on overflow. This can easily result
     * in bugs, because programmers usually assume that an overflow raises an
     * error, which is the standard behavior in high level programming languages.
     * `SafeMath` restores this intuition by reverting the transaction when an
     * operation overflows.
     *
     * Using this library instead of the unchecked operations eliminates an entire
     * class of bugs, so it's recommended to use it always.
     */
    library SafeMath {
        /**
         * @dev Returns the addition of two unsigned integers, reverting on
         * overflow.
         *
         * Counterpart to Solidity's `+` operator.
         *
         * Requirements:
         * - Addition cannot overflow.
         */
        function add(uint256 a, uint256 b) internal pure returns (uint256) {
            uint256 c = a + b;
            require(c >= a, "SafeMath: addition overflow");
    
            return c;
        }
    
        /**
         * @dev Returns the subtraction of two unsigned integers, reverting on
         * overflow (when the result is negative).
         *
         * Counterpart to Solidity's `-` operator.
         *
         * Requirements:
         * - Subtraction cannot overflow.
         */
        function sub(uint256 a, uint256 b) internal pure returns (uint256) {
            return sub(a, b, "SafeMath: subtraction overflow");
        }
    
        /**
         * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
         * overflow (when the result is negative).
         *
         * Counterpart to Solidity's `-` operator.
         *
         * Requirements:
         * - Subtraction cannot overflow.
         *
         * _Available since v2.4.0._
         */
        function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
            require(b <= a, errorMessage);
            uint256 c = a - b;
    
            return c;
        }
    
        /**
         * @dev Returns the multiplication of two unsigned integers, reverting on
         * overflow.
         *
         * Counterpart to Solidity's `*` operator.
         *
         * Requirements:
         * - Multiplication cannot overflow.
         */
        function mul(uint256 a, uint256 b) internal pure returns (uint256) {
            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
            // benefit is lost if 'b' is also tested.
            // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
            if (a == 0) {
                return 0;
            }
    
            uint256 c = a * b;
            require(c / a == b, "SafeMath: multiplication overflow");
    
            return c;
        }
    
        /**
         * @dev Returns the integer division of two unsigned integers. Reverts on
         * division by zero. The result is rounded towards zero.
         *
         * Counterpart to Solidity's `/` operator. Note: this function uses a
         * `revert` opcode (which leaves remaining gas untouched) while Solidity
         * uses an invalid opcode to revert (consuming all remaining gas).
         *
         * Requirements:
         * - The divisor cannot be zero.
         */
        function div(uint256 a, uint256 b) internal pure returns (uint256) {
            return div(a, b, "SafeMath: division by zero");
        }
    
        /**
         * @dev Returns the integer division of two unsigned integers. Reverts with custom message on
         * division by zero. The result is rounded towards zero.
         *
         * Counterpart to Solidity's `/` operator. Note: this function uses a
         * `revert` opcode (which leaves remaining gas untouched) while Solidity
         * uses an invalid opcode to revert (consuming all remaining gas).
         *
         * Requirements:
         * - The divisor cannot be zero.
         *
         * _Available since v2.4.0._
         */
        function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
            // Solidity only automatically asserts when dividing by 0
            require(b > 0, errorMessage);
            uint256 c = a / b;
            // assert(a == b * c + a % b); // There is no case in which this doesn't hold
    
            return c;
        }
    
        /**
         * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
         * Reverts when dividing by zero.
         *
         * Counterpart to Solidity's `%` operator. This function uses a `revert`
         * opcode (which leaves remaining gas untouched) while Solidity uses an
         * invalid opcode to revert (consuming all remaining gas).
         *
         * Requirements:
         * - The divisor cannot be zero.
         */
        function mod(uint256 a, uint256 b) internal pure returns (uint256) {
            return mod(a, b, "SafeMath: modulo by zero");
        }
    
        /**
         * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
         * Reverts with custom message when dividing by zero.
         *
         * Counterpart to Solidity's `%` operator. This function uses a `revert`
         * opcode (which leaves remaining gas untouched) while Solidity uses an
         * invalid opcode to revert (consuming all remaining gas).
         *
         * Requirements:
         * - The divisor cannot be zero.
         *
         * _Available since v2.4.0._
         */
        function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
            require(b != 0, errorMessage);
            return a % b;
        }
    }
    
    /**
     * @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) {
            // According to EIP-1052, 0x0 is the value returned for not-yet created accounts
            // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
            // for accounts without code, i.e. `keccak256('')`
            bytes32 codehash;
            bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
            // solhint-disable-next-line no-inline-assembly
            assembly { codehash := extcodehash(account) }
            return (codehash != accountHash && codehash != 0x0);
        }
    
        /**
         * @dev Converts an `address` into `address payable`. Note that this is
         * simply a type cast: the actual underlying value is not changed.
         *
         * _Available since v2.4.0._
         */
        function toPayable(address account) internal pure returns (address payable) {
            return address(uint160(account));
        }
    
        /**
         * @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].
         *
         * _Available since v2.4.0._
         */
        function sendValue(address payable recipient, uint256 amount) internal {
            require(address(this).balance >= amount, "Address: insufficient balance");
    
            // solhint-disable-next-line avoid-call-value
            (bool success, ) = recipient.call.value(amount)("");
            require(success, "Address: unable to send value, recipient may have reverted");
        }
    }
    
    library BytesLib {
        function concat(
            bytes memory _preBytes,
            bytes memory _postBytes
        )
        internal
        pure
        returns (bytes memory)
        {
            bytes memory tempBytes;
    
            assembly {
            // Get a location of some free memory and store it in tempBytes as
            // Solidity does for memory variables.
                tempBytes := mload(0x40)
    
            // Store the length of the first bytes array at the beginning of
            // the memory for tempBytes.
                let length := mload(_preBytes)
                mstore(tempBytes, length)
    
            // Maintain a memory counter for the current write location in the
            // temp bytes array by adding the 32 bytes for the array length to
            // the starting location.
                let mc := add(tempBytes, 0x20)
            // Stop copying when the memory counter reaches the length of the
            // first bytes array.
                let end := add(mc, length)
    
                for {
                // Initialize a copy counter to the start of the _preBytes data,
                // 32 bytes into its memory.
                    let cc := add(_preBytes, 0x20)
                } lt(mc, end) {
                // Increase both counters by 32 bytes each iteration.
                    mc := add(mc, 0x20)
                    cc := add(cc, 0x20)
                } {
                // Write the _preBytes data into the tempBytes memory 32 bytes
                // at a time.
                    mstore(mc, mload(cc))
                }
    
            // Add the length of _postBytes to the current length of tempBytes
            // and store it as the new length in the first 32 bytes of the
            // tempBytes memory.
                length := mload(_postBytes)
                mstore(tempBytes, add(length, mload(tempBytes)))
    
            // Move the memory counter back from a multiple of 0x20 to the
            // actual end of the _preBytes data.
                mc := end
            // Stop copying when the memory counter reaches the new combined
            // length of the arrays.
                end := add(mc, length)
    
                for {
                    let cc := add(_postBytes, 0x20)
                } lt(mc, end) {
                    mc := add(mc, 0x20)
                    cc := add(cc, 0x20)
                } {
                    mstore(mc, mload(cc))
                }
    
            // Update the free-memory pointer by padding our last write location
            // to 32 bytes: add 31 bytes to the end of tempBytes to move to the
            // next 32 byte block, then round down to the nearest multiple of
            // 32. If the sum of the length of the two arrays is zero then add
            // one before rounding down to leave a blank 32 bytes (the length block with 0).
                mstore(0x40, and(
                add(add(end, iszero(add(length, mload(_preBytes)))), 31),
                not(31) // Round down to the nearest 32 bytes.
                ))
            }
    
            return tempBytes;
        }
    
        function concatStorage(bytes storage _preBytes, bytes memory _postBytes) internal {
            assembly {
            // Read the first 32 bytes of _preBytes storage, which is the length
            // of the array. (We don't need to use the offset into the slot
            // because arrays use the entire slot.)
                let fslot := sload(_preBytes_slot)
            // Arrays of 31 bytes or less have an even value in their slot,
            // while longer arrays have an odd value. The actual length is
            // the slot divided by two for odd values, and the lowest order
            // byte divided by two for even values.
            // If the slot is even, bitwise and the slot with 255 and divide by
            // two to get the length. If the slot is odd, bitwise and the slot
            // with -1 and divide by two.
                let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)
                let mlength := mload(_postBytes)
                let newlength := add(slength, mlength)
            // slength can contain both the length and contents of the array
            // if length < 32 bytes so let's prepare for that
            // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage
                switch add(lt(slength, 32), lt(newlength, 32))
                case 2 {
                // Since the new array still fits in the slot, we just need to
                // update the contents of the slot.
                // uint256(bytes_storage) = uint256(bytes_storage) + uint256(bytes_memory) + new_length
                    sstore(
                    _preBytes_slot,
                    // all the modifications to the slot are inside this
                    // next block
                    add(
                    // we can just add to the slot contents because the
                    // bytes we want to change are the LSBs
                    fslot,
                    add(
                    mul(
                    div(
                    // load the bytes from memory
                    mload(add(_postBytes, 0x20)),
                    // zero all bytes to the right
                    exp(0x100, sub(32, mlength))
                    ),
                    // and now shift left the number of bytes to
                    // leave space for the length in the slot
                    exp(0x100, sub(32, newlength))
                    ),
                    // increase length by the double of the memory
                    // bytes length
                    mul(mlength, 2)
                    )
                    )
                    )
                }
                case 1 {
                // The stored value fits in the slot, but the combined value
                // will exceed it.
                // get the keccak hash to get the contents of the array
                    mstore(0x0, _preBytes_slot)
                    let sc := add(keccak256(0x0, 0x20), div(slength, 32))
    
                // save new length
                    sstore(_preBytes_slot, add(mul(newlength, 2), 1))
    
                // The contents of the _postBytes array start 32 bytes into
                // the structure. Our first read should obtain the `submod`
                // bytes that can fit into the unused space in the last word
                // of the stored array. To get this, we read 32 bytes starting
                // from `submod`, so the data we read overlaps with the array
                // contents by `submod` bytes. Masking the lowest-order
                // `submod` bytes allows us to add that value directly to the
                // stored value.
    
                    let submod := sub(32, slength)
                    let mc := add(_postBytes, submod)
                    let end := add(_postBytes, mlength)
                    let mask := sub(exp(0x100, submod), 1)
    
                    sstore(
                    sc,
                    add(
                    and(
                    fslot,
                    0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00
                    ),
                    and(mload(mc), mask)
                    )
                    )
    
                    for {
                        mc := add(mc, 0x20)
                        sc := add(sc, 1)
                    } lt(mc, end) {
                        sc := add(sc, 1)
                        mc := add(mc, 0x20)
                    } {
                        sstore(sc, mload(mc))
                    }
    
                    mask := exp(0x100, sub(mc, end))
    
                    sstore(sc, mul(div(mload(mc), mask), mask))
                }
                default {
                // get the keccak hash to get the contents of the array
                    mstore(0x0, _preBytes_slot)
                // Start copying to the last used word of the stored array.
                    let sc := add(keccak256(0x0, 0x20), div(slength, 32))
    
                // save new length
                    sstore(_preBytes_slot, add(mul(newlength, 2), 1))
    
                // Copy over the first `submod` bytes of the new data as in
                // case 1 above.
                    let slengthmod := mod(slength, 32)
                    let mlengthmod := mod(mlength, 32)
                    let submod := sub(32, slengthmod)
                    let mc := add(_postBytes, submod)
                    let end := add(_postBytes, mlength)
                    let mask := sub(exp(0x100, submod), 1)
    
                    sstore(sc, add(sload(sc), and(mload(mc), mask)))
    
                    for {
                        sc := add(sc, 1)
                        mc := add(mc, 0x20)
                    } lt(mc, end) {
                        sc := add(sc, 1)
                        mc := add(mc, 0x20)
                    } {
                        sstore(sc, mload(mc))
                    }
    
                    mask := exp(0x100, sub(mc, end))
    
                    sstore(sc, mul(div(mload(mc), mask), mask))
                }
            }
        }
    
        function slice(
            bytes memory _bytes,
            uint _start,
            uint _length
        )
        internal
        pure
        returns (bytes memory)
        {
            require(_bytes.length >= (_start + _length));
    
            bytes memory tempBytes;
    
            assembly {
                switch iszero(_length)
                case 0 {
                // Get a location of some free memory and store it in tempBytes as
                // Solidity does for memory variables.
                    tempBytes := mload(0x40)
    
                // The first word of the slice result is potentially a partial
                // word read from the original array. To read it, we calculate
                // the length of that partial word and start copying that many
                // bytes into the array. The first word we copy will start with
                // data we don't care about, but the last `lengthmod` bytes will
                // land at the beginning of the contents of the new array. When
                // we're done copying, we overwrite the full first word with
                // the actual length of the slice.
                    let lengthmod := and(_length, 31)
    
                // The multiplication in the next line is necessary
                // because when slicing multiples of 32 bytes (lengthmod == 0)
                // the following copy loop was copying the origin's length
                // and then ending prematurely not copying everything it should.
                    let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))
                    let end := add(mc, _length)
    
                    for {
                    // The multiplication in the next line has the same exact purpose
                    // as the one above.
                        let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start)
                    } lt(mc, end) {
                        mc := add(mc, 0x20)
                        cc := add(cc, 0x20)
                    } {
                        mstore(mc, mload(cc))
                    }
    
                    mstore(tempBytes, _length)
    
                //update free-memory pointer
                //allocating the array padded to 32 bytes like the compiler does now
                    mstore(0x40, and(add(mc, 31), not(31)))
                }
                //if we want a zero-length slice let's just return a zero-length array
                default {
                    tempBytes := mload(0x40)
    
                    mstore(0x40, add(tempBytes, 0x20))
                }
            }
    
            return tempBytes;
        }
    
        function toAddress(bytes memory _bytes, uint _start) internal  pure returns (address) {
            require(_bytes.length >= (_start + 20));
            address tempAddress;
    
            assembly {
                tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000)
            }
    
            return tempAddress;
        }
    
        function toUint8(bytes memory _bytes, uint _start) internal  pure returns (uint8) {
            require(_bytes.length >= (_start + 1));
            uint8 tempUint;
    
            assembly {
                tempUint := mload(add(add(_bytes, 0x1), _start))
            }
    
            return tempUint;
        }
    
        function toUint16(bytes memory _bytes, uint _start) internal  pure returns (uint16) {
            require(_bytes.length >= (_start + 2));
            uint16 tempUint;
    
            assembly {
                tempUint := mload(add(add(_bytes, 0x2), _start))
            }
    
            return tempUint;
        }
    
        function toUint32(bytes memory _bytes, uint _start) internal  pure returns (uint32) {
            require(_bytes.length >= (_start + 4));
            uint32 tempUint;
    
            assembly {
                tempUint := mload(add(add(_bytes, 0x4), _start))
            }
    
            return tempUint;
        }
    
        function toUint64(bytes memory _bytes, uint _start) internal  pure returns (uint64) {
            require(_bytes.length >= (_start + 8));
            uint64 tempUint;
    
            assembly {
                tempUint := mload(add(add(_bytes, 0x8), _start))
            }
    
            return tempUint;
        }
    
        function toUint96(bytes memory _bytes, uint _start) internal  pure returns (uint96) {
            require(_bytes.length >= (_start + 12));
            uint96 tempUint;
    
            assembly {
                tempUint := mload(add(add(_bytes, 0xc), _start))
            }
    
            return tempUint;
        }
    
        function toUint128(bytes memory _bytes, uint _start) internal  pure returns (uint128) {
            require(_bytes.length >= (_start + 16));
            uint128 tempUint;
    
            assembly {
                tempUint := mload(add(add(_bytes, 0x10), _start))
            }
    
            return tempUint;
        }
    
        function toUint(bytes memory _bytes, uint _start) internal  pure returns (uint256) {
            require(_bytes.length >= (_start + 32));
            uint256 tempUint;
    
            assembly {
                tempUint := mload(add(add(_bytes, 0x20), _start))
            }
    
            return tempUint;
        }
    
        function toBytes32(bytes memory _bytes, uint _start) internal  pure returns (bytes32) {
            require(_bytes.length >= (_start + 32));
            bytes32 tempBytes32;
    
            assembly {
                tempBytes32 := mload(add(add(_bytes, 0x20), _start))
            }
    
            return tempBytes32;
        }
    
        function equal(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bool) {
            bool success = true;
    
            assembly {
                let length := mload(_preBytes)
    
            // if lengths don't match the arrays are not equal
                switch eq(length, mload(_postBytes))
                case 1 {
                // cb is a circuit breaker in the for loop since there's
                //  no said feature for inline assembly loops
                // cb = 1 - don't breaker
                // cb = 0 - break
                    let cb := 1
    
                    let mc := add(_preBytes, 0x20)
                    let end := add(mc, length)
    
                    for {
                        let cc := add(_postBytes, 0x20)
                    // the next line is the loop condition:
                    // while(uint(mc < end) + cb == 2)
                    } eq(add(lt(mc, end), cb), 2) {
                        mc := add(mc, 0x20)
                        cc := add(cc, 0x20)
                    } {
                    // if any of these checks fails then arrays are not equal
                        if iszero(eq(mload(mc), mload(cc))) {
                        // unsuccess:
                            success := 0
                            cb := 0
                        }
                    }
                }
                default {
                // unsuccess:
                    success := 0
                }
            }
    
            return success;
        }
    
        function equalStorage(
            bytes storage _preBytes,
            bytes memory _postBytes
        )
        internal
        view
        returns (bool)
        {
            bool success = true;
    
            assembly {
            // we know _preBytes_offset is 0
                let fslot := sload(_preBytes_slot)
            // Decode the length of the stored array like in concatStorage().
                let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)
                let mlength := mload(_postBytes)
    
            // if lengths don't match the arrays are not equal
                switch eq(slength, mlength)
                case 1 {
                // slength can contain both the length and contents of the array
                // if length < 32 bytes so let's prepare for that
                // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage
                    if iszero(iszero(slength)) {
                        switch lt(slength, 32)
                        case 1 {
                        // blank the last byte which is the length
                            fslot := mul(div(fslot, 0x100), 0x100)
    
                            if iszero(eq(fslot, mload(add(_postBytes, 0x20)))) {
                            // unsuccess:
                                success := 0
                            }
                        }
                        default {
                        // cb is a circuit breaker in the for loop since there's
                        //  no said feature for inline assembly loops
                        // cb = 1 - don't breaker
                        // cb = 0 - break
                            let cb := 1
    
                        // get the keccak hash to get the contents of the array
                            mstore(0x0, _preBytes_slot)
                            let sc := keccak256(0x0, 0x20)
    
                            let mc := add(_postBytes, 0x20)
                            let end := add(mc, mlength)
    
                        // the next line is the loop condition:
                        // while(uint(mc < end) + cb == 2)
                            for {} eq(add(lt(mc, end), cb), 2) {
                                sc := add(sc, 1)
                                mc := add(mc, 0x20)
                            } {
                                if iszero(eq(sload(sc), mload(mc))) {
                                // unsuccess:
                                    success := 0
                                    cb := 0
                                }
                            }
                        }
                    }
                }
                default {
                // unsuccess:
                    success := 0
                }
            }
    
            return success;
        }
    }
    
    interface IERC20Minter {
        function mint(address to, uint256 amount) external;
        function burn(uint256 amount) external;
        function replaceMinter(address newMinter) external;
    }
    
    contract NerveMultiSigWalletIII is ReentrancyGuard {
        using Address for address;
        using SafeERC20 for IERC20;
        using SafeMath for uint256;
        using BytesLib for bytes;
    
        modifier isOwner{
            require(owner == msg.sender, "Only owner can execute it");
            _;
        }
        modifier isManager{
            require(managers[msg.sender] == 1, "Only manager can execute it");
            _;
        }
        bool public upgrade = false;
        address public upgradeContractAddress = address(0);
        // 最大管理员数量
        uint public max_managers = 15;
        // 最小管理员数量
        uint public min_managers = 3;
        // 最小签名比例 66%
        uint public rate = 66;
        // 签名字节长度
        uint public signatureLength = 65;
        // 比例分母
        uint constant DENOMINATOR = 100;
        // 当前合约版本
        uint8 constant VERSION = 3;
        // hash计算加盐
        uint public hashSalt; 
        // 当前交易的最小签名数量
        uint8 public current_min_signatures;
        address public owner;
        mapping(address => uint8) private seedManagers;
        address[] private seedManagerArray;
        mapping(address => uint8) private managers;
        address[] private managerArray;
        mapping(bytes32 => uint8) private completedKeccak256s;
        mapping(string => uint8) private completedTxs;
        mapping(address => uint8) private minterERC20s;
        mapping(address => uint8) public bugERC20s;
        bool public openCrossOutII = false;
    
        constructor(uint256 _chainid, address[] memory _managers) public{
            require(_managers.length <= max_managers, "Exceeded the maximum number of managers");
            require(_managers.length >= min_managers, "Not reaching the min number of managers");
            owner = msg.sender;
            managerArray = _managers;
            for (uint8 i = 0; i < managerArray.length; i++) {
                managers[managerArray[i]] = 1;
                seedManagers[managerArray[i]] = 1;
                seedManagerArray.push(managerArray[i]);
            }
            require(managers[owner] == 0, "Contract creator cannot act as manager");
            // 设置当前交易的最小签名数量
            current_min_signatures = calMinSignatures(managerArray.length);
            hashSalt = _chainid * 2 + VERSION;
        }
        function() external payable {
            emit DepositFunds(msg.sender, msg.value);
        }
    
        function createOrSignWithdraw(string memory txKey, address payable to, uint256 amount, bool isERC20, address ERC20, bytes memory signatures) public nonReentrant isManager {
            require(bytes(txKey).length == 64, "Fixed length of txKey: 64");
            require(to != address(0), "Withdraw: transfer to the zero address");
            require(amount > 0, "Withdrawal amount must be greater than 0");
            // 校验已经完成的交易
            require(completedTxs[txKey] == 0, "Transaction has been completed");
            // 校验提现金额
            if (isERC20) {
                validateTransferERC20(ERC20, to, amount);
            } else {
                require(address(this).balance >= amount, "This contract address does not have sufficient balance of ether");
            }
            bytes32 vHash = keccak256(abi.encodePacked(txKey, to, amount, isERC20, ERC20, hashSalt));
            // 校验请求重复性
            require(completedKeccak256s[vHash] == 0, "Invalid signatures");
            // 校验签名
            require(validSignature(vHash, signatures), "Valid signatures fail");
            // 执行转账
            if (isERC20) {
                transferERC20(ERC20, to, amount);
            } else {
                // 实际到账
                require(address(this).balance >= amount, "This contract address does not have sufficient balance of ether");
                to.transfer(amount);
                emit TransferFunds(to, amount);
            }
            // 保存交易数据
            completeTx(txKey, vHash, 1);
            emit TxWithdrawCompleted(txKey);
        }
    
    
        function createOrSignManagerChange(string memory txKey, address[] memory adds, address[] memory removes, uint8 count, bytes memory signatures) public isManager {
            require(bytes(txKey).length == 64, "Fixed length of txKey: 64");
            require(adds.length > 0 || removes.length > 0, "There are no managers joining or exiting");
            // 校验已经完成的交易
            require(completedTxs[txKey] == 0, "Transaction has been completed");
            preValidateAddsAndRemoves(adds, removes);
            bytes32 vHash = keccak256(abi.encodePacked(txKey, adds, count, removes, hashSalt));
            // 校验请求重复性
            require(completedKeccak256s[vHash] == 0, "Invalid signatures");
            // 校验签名
            require(validSignature(vHash, signatures), "Valid signatures fail");
            // 变更管理员
            removeManager(removes);
            addManager(adds);
            // 更新当前交易的最小签名数
            current_min_signatures = calMinSignatures(managerArray.length);
            // 保存交易数据
            completeTx(txKey, vHash, 1);
            // add event
            emit TxManagerChangeCompleted(txKey);
        }
    
        function createOrSignUpgrade(string memory txKey, address upgradeContract, bytes memory signatures) public isManager {
            require(bytes(txKey).length == 64, "Fixed length of txKey: 64");
            // 校验已经完成的交易
            require(completedTxs[txKey] == 0, "Transaction has been completed");
            require(!upgrade, "It has been upgraded");
            require(upgradeContract.isContract(), "The address is not a contract address");
            // 校验
            bytes32 vHash = keccak256(abi.encodePacked(txKey, upgradeContract, hashSalt));
            // 校验请求重复性
            require(completedKeccak256s[vHash] == 0, "Invalid signatures");
            // 校验签名
            require(validSignature(vHash, signatures), "Valid signatures fail");
            // 变更可升级
            upgrade = true;
            upgradeContractAddress = upgradeContract;
            // 保存交易数据
            completeTx(txKey, vHash, 1);
            // add event
            emit TxUpgradeCompleted(txKey);
        }
    
        function validSignature(bytes32 hash, bytes memory signatures) internal view returns (bool) {
            require(signatures.length <= 975, "Max length of signatures: 975");
            // 获取签名列表对应的有效管理员,如果存在错误的签名、或者不是管理员的签名,就失败
            uint sManagersCount = getManagerFromSignatures(hash, signatures);
            // 判断最小签名数量
            return sManagersCount >= current_min_signatures;
        }
    
        function getManagerFromSignatures(bytes32 hash, bytes memory signatures) internal view returns (uint){
            uint signCount = 0;
            uint times = signatures.length.div(signatureLength);
            address[] memory result = new address[](times);
            uint k = 0;
            uint8 j = 0;
            for (uint i = 0; i < times; i++) {
                bytes memory sign = signatures.slice(k, signatureLength);
                address mAddress = ecrecovery(hash, sign);
                require(mAddress != address(0), "Signatures error");
                // 管理计数
                if (managers[mAddress] == 1) {
                    signCount++;
                    result[j++] = mAddress;
                }
                k += signatureLength;
            }
            // 验证地址重复性
            bool suc = repeatability(result);
            delete result;
            require(suc, "Signatures duplicate");
            return signCount;
        }
    
        function validateRepeatability(address currentAddress, address[] memory list) internal pure returns (bool) {
            address tempAddress;
            for (uint i = 0; i < list.length; i++) {
                tempAddress = list[i];
                if (tempAddress == address(0)) {
                    break;
                }
                if (tempAddress == currentAddress) {
                    return false;
                }
            }
            return true;
        }
    
        function repeatability(address[] memory list) internal pure returns (bool) {
            for (uint i = 0; i < list.length; i++) {
                address address1 = list[i];
                if (address1 == address(0)) {
                    break;
                }
                for (uint j = i + 1; j < list.length; j++) {
                    address address2 = list[j];
                    if (address2 == address(0)) {
                        break;
                    }
                    if (address1 == address2) {
                        return false;
                    }
                }
            }
            return true;
        }
    
        function ecrecovery(bytes32 hash, bytes memory sig) internal view returns (address) {
            bytes32 r;
            bytes32 s;
            uint8 v;
            if (sig.length != signatureLength) {
                return address(0);
            }
            assembly {
                r := mload(add(sig, 32))
                s := mload(add(sig, 64))
                v := byte(0, mload(add(sig, 96)))
            }
            if(uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
                return address(0);
            }
            // https://github.com/ethereum/go-ethereum/issues/2053
            if (v < 27) {
                v += 27;
            }
            if (v != 27 && v != 28) {
                return address(0);
            }
            return ecrecover(hash, v, r, s);
        }
    
        function preValidateAddsAndRemoves(address[] memory adds, address[] memory removes) internal view {
            // 校验adds
            uint addLen = adds.length;
            for (uint i = 0; i < addLen; i++) {
                address add = adds[i];
                require(add != address(0), "ERROR: Detected zero address in adds");
                require(managers[add] == 0, "The address list that is being added already exists as a manager");
            }
            require(repeatability(adds), "Duplicate parameters for the address to join");
            // 校验合约创建者不能被添加
            require(validateRepeatability(owner, adds), "Contract creator cannot act as manager");
            // 校验removes
            require(repeatability(removes), "Duplicate parameters for the address to exit");
            uint removeLen = removes.length;
            for (uint i = 0; i < removeLen; i++) {
                address remove = removes[i];
                require(seedManagers[remove] == 0, "Can't exit seed manager");
                require(managers[remove] == 1, "There are addresses in the exiting address list that are not manager");
            }
            require(managerArray.length + adds.length - removes.length <= max_managers, "Exceeded the maximum number of managers");
        }
    
        /*
         根据 `当前有效管理员数量` 和 `最小签名比例` 计算最小签名数量,向上取整
        */
        function calMinSignatures(uint managerCounts) internal view returns (uint8) {
            require(managerCounts > 0, "Manager Can't empty.");
            uint numerator = rate * managerCounts + DENOMINATOR - 1;
            return uint8(numerator / DENOMINATOR);
        }
        function removeManager(address[] memory removes) internal {
            if (removes.length == 0) {
                return;
            }
            for (uint i = 0; i < removes.length; i++) {
                delete managers[removes[i]];
            }
            // 遍历修改前管理员列表
            for (uint i = 0; i < managerArray.length; i++) {
                if (managers[managerArray[i]] == 0) {
                    delete managerArray[i];
                }
            }
            uint tempIndex = 0x10;
            for (uint i = 0; i<managerArray.length; i++) {
                address temp = managerArray[i];
                if (temp == address(0)) {
                    if (tempIndex == 0x10) tempIndex = i;
                    continue;
                } else if (tempIndex != 0x10) {
                    managerArray[tempIndex] = temp;
                    tempIndex++;
                }
            }
            managerArray.length -= removes.length;
        }
        function addManager(address[] memory adds) internal {
            if (adds.length == 0) {
                return;
            }
            for (uint i = 0; i < adds.length; i++) {
                address add = adds[i];
                if(managers[add] == 0) {
                    managers[add] = 1;
                    managerArray.push(add);
                }
            }
        }
        function completeTx(string memory txKey, bytes32 keccak256Hash, uint8 e) internal {
            completedTxs[txKey] = e;
            completedKeccak256s[keccak256Hash] = e;
        }
        function validateTransferERC20(address ERC20, address to, uint256 amount) internal view {
            require(to != address(0), "ERC20: transfer to the zero address");
            require(address(this) != ERC20, "Do nothing by yourself");
            require(ERC20.isContract(), "The address is not a contract address");
            if (isMinterERC20(ERC20)) {
                // 定制ERC20验证结束
                return;
            }
            IERC20 token = IERC20(ERC20);
            uint256 balance = token.balanceOf(address(this));
            require(balance >= amount, "No enough balance of token");
        }
        function transferERC20(address ERC20, address to, uint256 amount) internal {
            if (isMinterERC20(ERC20)) {
                // 定制的ERC20,跨链转入以太坊网络即增发
                IERC20Minter minterToken = IERC20Minter(ERC20);
                minterToken.mint(to, amount);
                return;
            }
            IERC20 token = IERC20(ERC20);
            uint256 balance = token.balanceOf(address(this));
            require(balance >= amount, "No enough balance of token");
            token.safeTransfer(to, amount, bugERC20s);
        }
        function closeUpgrade() public isOwner {
            require(upgrade, "Denied");
            upgrade = false;
        }
        function upgradeContractS1() public isOwner {
            require(upgrade, "Denied");
            require(upgradeContractAddress != address(0), "ERROR: transfer to the zero address");
            address(uint160(upgradeContractAddress)).transfer(address(this).balance);
        }
        function upgradeContractS2(address ERC20) public isOwner {
            require(upgrade, "Denied");
            require(upgradeContractAddress != address(0), "ERROR: transfer to the zero address");
            require(address(this) != ERC20, "Do nothing by yourself");
            require(ERC20.isContract(), "The address is not a contract address");
            IERC20 token = IERC20(ERC20);
            uint256 balance = token.balanceOf(address(this));
            require(balance >= 0, "No enough balance of token");
            token.safeTransfer(upgradeContractAddress, balance, bugERC20s);
            if (isMinterERC20(ERC20)) {
                // 定制的ERC20,转移增发销毁权限到新多签合约
                IERC20Minter minterToken = IERC20Minter(ERC20);
                minterToken.replaceMinter(upgradeContractAddress);
            }
        }
    
        // 是否定制的ERC20
        function isMinterERC20(address ERC20) public view returns (bool) {
            return minterERC20s[ERC20] > 0;
        }
    
        // 登记定制的ERC20
        function registerMinterERC20(address ERC20) public isOwner {
            require(address(this) != ERC20, "Do nothing by yourself");
            require(ERC20.isContract(), "The address is not a contract address");
            require(!isMinterERC20(ERC20), "This address has already been registered");
            minterERC20s[ERC20] = 1;
        }
    
        // 取消登记定制的ERC20
        function unregisterMinterERC20(address ERC20) public isOwner {
            require(isMinterERC20(ERC20), "This address is not registered");
            delete minterERC20s[ERC20];
        }
    
        // 登记BUG的ERC20
        function registerBugERC20(address bug) public isOwner {
            require(address(this) != bug, "Do nothing by yourself");
            require(bug.isContract(), "The address is not a contract address");
            bugERC20s[bug] = 1;
        }
        // 取消登记BUG的ERC20
        function unregisterBugERC20(address bug) public isOwner {
            bugERC20s[bug] = 0;
        }
        // 从eth网络跨链转出资产(ETH or ERC20)
        function crossOut(string memory to, uint256 amount, address ERC20) public payable returns (bool) {
            address from = msg.sender;
            require(amount > 0, "ERROR: Zero amount");
            if (ERC20 != address(0)) {
                require(msg.value == 0, "ERC20: Does not accept Ethereum Coin");
                require(ERC20.isContract(), "The address is not a contract address");
                IERC20 token = IERC20(ERC20);
                uint256 allowance = token.allowance(from, address(this));
                require(allowance >= amount, "No enough amount for authorization");
                uint256 fromBalance = token.balanceOf(from);
                require(fromBalance >= amount, "No enough balance of the token");
                token.safeTransferFrom(from, address(this), amount, bugERC20s);
                if (isMinterERC20(ERC20)) {
                    // 定制的ERC20,从以太坊网络跨链转出token即销毁
                    IERC20Minter minterToken = IERC20Minter(ERC20);
                    minterToken.burn(amount);
                }
            } else {
                require(msg.value == amount, "Inconsistency Ethereum amount");
            }
            emit CrossOutFunds(from, to, amount, ERC20);
            return true;
        }
    
        // 从eth网络跨链转出资产(ETH or ERC20)
        function crossOutII(string memory to, uint256 amount, address ERC20, bytes memory data) public payable returns (bool) {
            require(openCrossOutII, "CrossOutII: Not open");
            address from = msg.sender;
            uint erc20Amount = 0;
            if (ERC20 != address(0)) {
                require(amount > 0, "ERROR: Zero amount");
                require(ERC20.isContract(), "The address is not a contract address");
                IERC20 token = IERC20(ERC20);
                uint256 allowance = token.allowance(from, address(this));
                require(allowance >= amount, "No enough amount for authorization");
                uint256 fromBalance = token.balanceOf(from);
                require(fromBalance >= amount, "No enough balance of the token");
                token.safeTransferFrom(from, address(this), amount, bugERC20s);
                if (isMinterERC20(ERC20)) {
                    // 定制的ERC20,从以太坊网络跨链转出token即销毁
                    IERC20Minter minterToken = IERC20Minter(ERC20);
                    minterToken.burn(amount);
                }
                erc20Amount = amount;
            } else {
                require(msg.value > 0 && amount == 0, "CrossOutII: Illegal eth amount");
            }
            emit CrossOutIIFunds(from, to, erc20Amount, ERC20, msg.value, data);
            return true;
        }
    
        function setCrossOutII(bool _open) public isOwner {
            openCrossOutII = _open;
        }
    
        function isCompletedTx(string memory txKey) public view returns (bool){
            return completedTxs[txKey] > 0;
        }
        function ifManager(address _manager) public view returns (bool) {
            return managers[_manager] == 1;
        }
        function allManagers() public view returns (address[] memory) {
            return managerArray;
        }
        event DepositFunds(address from, uint amount);
        event CrossOutFunds(address from, string to, uint amount, address ERC20);
        event CrossOutIIFunds(address from, string to, uint amount, address ERC20, uint ethAmount, bytes data);
        event TransferFunds(address to, uint amount);
        event TxWithdrawCompleted(string txKey);
        event TxManagerChangeCompleted(string txKey);
        event TxUpgradeCompleted(string txKey);
    }