ETH Price: $2,521.20 (+0.15%)

Transaction Decoder

Block:
22824637 at Jul-01-2025 01:18:35 PM +UTC
Transaction Fee:
0.000240867874328125 ETH $0.61
Gas Used:
56,375 Gas / 4.272600875 Gwei

Emitted Events:

384 FlexaCollateralManager.ReleaseRequest( supplier=[Receiver] 0x7f5c1cf5e107e0c916d6ec3857f75788500c9cc1, partition=CCCCCCCC2862B8CB21CAEDB8706D7F8B3445D8DFC790C524E3990EF014E7C578, amount=8417713993450031171041, data=0x )
385 0x7f5c1cf5e107e0c916d6ec3857f75788500c9cc1.0x509a412175404a94c1003e6e0f08ba539fc2c450d6376bc6263a2368e141c3da( 0x509a412175404a94c1003e6e0f08ba539fc2c450d6376bc6263a2368e141c3da, 0x00000000000000000000000037b31c7239b6d7c38801bf32831c2bed66e7c1b6, 0x000000000000000000000000706d7f8b3445d8dfc790c524e3990ef014e7c578 )
386 FlexaCollateralManager.ReleaseRequest( supplier=[Receiver] 0x7f5c1cf5e107e0c916d6ec3857f75788500c9cc1, partition=CCCCCCCC7A0208A97D8AC263706D7F8B3445D8DFC790C524E3990EF014E7C578, amount=10909234471228015216054, data=0x )
387 0x7f5c1cf5e107e0c916d6ec3857f75788500c9cc1.0x509a412175404a94c1003e6e0f08ba539fc2c450d6376bc6263a2368e141c3da( 0x509a412175404a94c1003e6e0f08ba539fc2c450d6376bc6263a2368e141c3da, 0x00000000000000000000000037b31c7239b6d7c38801bf32831c2bed66e7c1b6, 0x000000000000000000000000706d7f8b3445d8dfc790c524e3990ef014e7c578 )

Account State Difference:

  Address   Before After State Difference Code
0x37b31C72...D66E7C1B6
0.067559093680719927 Eth
Nonce: 1340
0.067318225806391802 Eth
Nonce: 1341
0.000240867874328125
(Titan Builder)
6.584731914540490446 Eth6.584745681742643821 Eth0.000013767202153375
0x7f5c1CF5...8500c9CC1 From: 22892027069488871726777845421492261844457874613051320068 To: 22892026638230306060177013438565229744157125598728899639

Execution Trace

0x7f5c1cf5e107e0c916d6ec3857f75788500c9cc1.3f707e6b( )
  • FlexaCollateralManager.requestRelease( _partition=CCCCCCCC2862B8CB21CAEDB8706D7F8B3445D8DFC790C524E3990EF014E7C578, _amount=8417713993450031171041, _data=0x )
  • FlexaCollateralManager.requestRelease( _partition=CCCCCCCC7A0208A97D8AC263706D7F8B3445D8DFC790C524E3990EF014E7C578, _amount=10909234471228015216054, _data=0x )
    // SPDX-License-Identifier: MIT
    
    pragma solidity 0.6.10;
    
    /**
     * @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.
         */
        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.
         */
        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.
         */
        function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
            require(b != 0, errorMessage);
            return a % b;
        }
    }
    
    interface IAmp {
        function registerCollateralManager() external;
    }
    
    /**
     * @title Ownable is a contract the provides contract ownership functionality, including a two-
     * phase transfer.
     */
    contract Ownable {
        address private _owner;
        address private _authorizedNewOwner;
    
        /**
         * @notice Emitted when the owner authorizes ownership transfer to a new address
         * @param authorizedAddress New owner address
         */
        event OwnershipTransferAuthorization(address indexed authorizedAddress);
    
        /**
         * @notice Emitted when the authorized address assumed ownership
         * @param oldValue Old owner
         * @param newValue New owner
         */
        event OwnerUpdate(address indexed oldValue, address indexed newValue);
    
        /**
         * @notice Sets the owner to the sender / contract creator
         */
        constructor() internal {
            _owner = msg.sender;
        }
    
        /**
         * @notice Retrieves the owner of the contract
         * @return The contract owner
         */
        function owner() public view returns (address) {
            return _owner;
        }
    
        /**
         * @notice Retrieves the authorized new owner of the contract
         * @return The authorized new contract owner
         */
        function authorizedNewOwner() public view returns (address) {
            return _authorizedNewOwner;
        }
    
        /**
         * @notice Authorizes the transfer of ownership from owner to the provided address.
         * NOTE: No transfer will occur unless authorizedAddress calls assumeOwnership().
         * This authorization may be removed by another call to this function authorizing the zero
         * address.
         * @param _authorizedAddress The address authorized to become the new owner
         */
        function authorizeOwnershipTransfer(address _authorizedAddress) external {
            require(msg.sender == _owner, "Invalid sender");
    
            _authorizedNewOwner = _authorizedAddress;
    
            emit OwnershipTransferAuthorization(_authorizedNewOwner);
        }
    
        /**
         * @notice Transfers ownership of this contract to the _authorizedNewOwner
         * @dev Error invalid sender.
         */
        function assumeOwnership() external {
            require(msg.sender == _authorizedNewOwner, "Invalid sender");
    
            address oldValue = _owner;
            _owner = _authorizedNewOwner;
            _authorizedNewOwner = address(0);
    
            emit OwnerUpdate(oldValue, _owner);
        }
    }
    
    abstract contract ERC1820Registry {
        function setInterfaceImplementer(
            address _addr,
            bytes32 _interfaceHash,
            address _implementer
        ) external virtual;
    
        function getInterfaceImplementer(address _addr, bytes32 _interfaceHash)
            external
            virtual
            view
            returns (address);
    
        function setManager(address _addr, address _newManager) external virtual;
    
        function getManager(address _addr) public virtual view returns (address);
    }
    
    /// Base client to interact with the registry.
    contract ERC1820Client {
        ERC1820Registry constant ERC1820REGISTRY = ERC1820Registry(
            0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24
        );
    
        function setInterfaceImplementation(
            string memory _interfaceLabel,
            address _implementation
        ) internal {
            bytes32 interfaceHash = keccak256(abi.encodePacked(_interfaceLabel));
            ERC1820REGISTRY.setInterfaceImplementer(
                address(this),
                interfaceHash,
                _implementation
            );
        }
    
        function interfaceAddr(address addr, string memory _interfaceLabel)
            internal
            view
            returns (address)
        {
            bytes32 interfaceHash = keccak256(abi.encodePacked(_interfaceLabel));
            return ERC1820REGISTRY.getInterfaceImplementer(addr, interfaceHash);
        }
    
        function delegateManagement(address _newManager) internal {
            ERC1820REGISTRY.setManager(address(this), _newManager);
        }
    }
    
    /**
     * @title IAmpTokensRecipient
     * @dev IAmpTokensRecipient token transfer hook interface
     */
    interface IAmpTokensRecipient {
        /**
         * @dev Report if the recipient will successfully receive the tokens
         */
        function canReceive(
            bytes4 functionSig,
            bytes32 partition,
            address operator,
            address from,
            address to,
            uint256 value,
            bytes calldata data,
            bytes calldata operatorData
        ) external view returns (bool);
    
        /**
         * @dev Hook executed upon a transfer to the recipient
         */
        function tokensReceived(
            bytes4 functionSig,
            bytes32 partition,
            address operator,
            address from,
            address to,
            uint256 value,
            bytes calldata data,
            bytes calldata operatorData
        ) external;
    }
    
    /**
     * @title IAmpTokensSender
     * @dev IAmpTokensSender token transfer hook interface
     */
    interface IAmpTokensSender {
        /**
         * @dev Report if the transfer will succeed from the pespective of the
         * token sender
         */
        function canTransfer(
            bytes4 functionSig,
            bytes32 partition,
            address operator,
            address from,
            address to,
            uint256 value,
            bytes calldata data,
            bytes calldata operatorData
        ) external view returns (bool);
    
        /**
         * @dev Hook executed upon a transfer on behalf of the sender
         */
        function tokensToTransfer(
            bytes4 functionSig,
            bytes32 partition,
            address operator,
            address from,
            address to,
            uint256 value,
            bytes calldata data,
            bytes calldata operatorData
        ) external;
    }
    
    /**
     * @title PartitionUtils
     * @notice Partition related helper functions.
     */
    
    library PartitionUtils {
        bytes32 public constant CHANGE_PARTITION_FLAG = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;
    
        /**
         * @notice Retrieve the destination partition from the 'data' field.
         * A partition change is requested ONLY when 'data' starts with the flag:
         *
         *   0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
         *
         * When the flag is detected, the destination partition is extracted from the
         * 32 bytes following the flag.
         * @param _data Information attached to the transfer. Will contain the
         * destination partition if a change is requested.
         * @param _fallbackPartition Partition value to return if a partition change
         * is not requested in the `_data`.
         * @return toPartition Destination partition. If the `_data` does not contain
         * the prefix and bytes32 partition in the first 64 bytes, the method will
         * return the provided `_fromPartition`.
         */
        function _getDestinationPartition(bytes memory _data, bytes32 _fallbackPartition)
            internal
            pure
            returns (bytes32)
        {
            if (_data.length < 64) {
                return _fallbackPartition;
            }
    
            (bytes32 flag, bytes32 toPartition) = abi.decode(_data, (bytes32, bytes32));
            if (flag == CHANGE_PARTITION_FLAG) {
                return toPartition;
            }
    
            return _fallbackPartition;
        }
    
        /**
         * @notice Helper to get the strategy identifying prefix from the `_partition`.
         * @param _partition Partition to get the prefix for.
         * @return 4 byte partition strategy prefix.
         */
        function _getPartitionPrefix(bytes32 _partition) internal pure returns (bytes4) {
            return bytes4(_partition);
        }
    
        /**
         * @notice Helper method to split the partition into the prefix, sub partition
         * and partition owner components.
         * @param _partition The partition to split into parts.
         * @return The 4 byte partition prefix, 8 byte sub partition, and final 20
         * bytes representing an address.
         */
        function _splitPartition(bytes32 _partition)
            internal
            pure
            returns (
                bytes4,
                bytes8,
                address
            )
        {
            bytes4 prefix = bytes4(_partition);
            bytes8 subPartition = bytes8(_partition << 32);
            address addressPart = address(uint160(uint256(_partition)));
            return (prefix, subPartition, addressPart);
        }
    
        /**
         * @notice Helper method to get a partition strategy ERC1820 interface name
         * based on partition prefix.
         * @param _prefix 4 byte partition prefix.
         * @dev Each 4 byte prefix has a unique interface name so that an individual
         * hook implementation can be set for each prefix.
         */
        function _getPartitionStrategyValidatorIName(bytes4 _prefix)
            internal
            pure
            returns (string memory)
        {
            return string(abi.encodePacked("AmpPartitionStrategyValidator", _prefix));
        }
    }
    
    /**
     * @title FlexaCollateralManager is an implementation of IAmpTokensSender and IAmpTokensRecipient
     * which serves as the Amp collateral manager for the Flexa Network.
     */
    contract FlexaCollateralManager is Ownable, IAmpTokensSender, IAmpTokensRecipient, ERC1820Client {
        /**
         * @dev AmpTokensSender interface label.
         */
        string internal constant AMP_TOKENS_SENDER = "AmpTokensSender";
    
        /**
         * @dev AmpTokensRecipient interface label.
         */
        string internal constant AMP_TOKENS_RECIPIENT = "AmpTokensRecipient";
    
        /**
         * @dev Change Partition Flag used in transfer data parameters to signal which partition
         * will receive the tokens.
         */
        bytes32
            internal constant CHANGE_PARTITION_FLAG = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;
    
        /**
         * @dev Required prefix for all registered partitions. Used to ensure the Collateral Pool
         * Partition Validator is used within Amp.
         */
        bytes4 internal constant PARTITION_PREFIX = 0xCCCCCCCC;
    
        /**********************************************************************************************
         * Operator Data Flags
         *********************************************************************************************/
    
        /**
         * @dev Flag used in operator data parameters to indicate the transfer is a withdrawal
         */
        bytes32
            internal constant WITHDRAWAL_FLAG = 0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa;
    
        /**
         * @dev Flag used in operator data parameters to indicate the transfer is a fallback
         * withdrawal
         */
        bytes32
            internal constant FALLBACK_WITHDRAWAL_FLAG = 0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb;
    
        /**
         * @dev Flag used in operator data parameters to indicate the transfer is a supply refund
         */
        bytes32
            internal constant REFUND_FLAG = 0xcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc;
    
        /**
         * @dev Flag used in operator data parameters to indicate the transfer is a direct transfer
         */
        bytes32
            internal constant DIRECT_TRANSFER_FLAG = 0xdddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd;
    
        /**********************************************************************************************
         * Configuration
         *********************************************************************************************/
    
        /**
         * @notice Address of the Amp contract. Immutable.
         */
        address public amp;
    
        /**
         * @notice Permitted partitions
         */
        mapping(bytes32 => bool) public partitions;
    
        /**********************************************************************************************
         * Roles
         *********************************************************************************************/
    
        /**
         * @notice Address authorized to publish withdrawal roots
         */
        address public withdrawalPublisher;
    
        /**
         * @notice Address authorized to publish fallback withdrawal roots
         */
        address public fallbackPublisher;
    
        /**
         * @notice Address authorized to adjust the withdrawal limit
         */
        address public withdrawalLimitPublisher;
    
        /**
         * @notice Address authorized to directly transfer tokens
         */
        address public directTransferer;
    
        /**
         * @notice Address authorized to manage permitted partition
         */
        address public partitionManager;
    
        /**
         * @notice Struct used to record received tokens that can be recovered during the fallback
         * withdrawal period
         * @param supplier Token supplier
         * @param partition Partition which received the tokens
         * @param amount Number of tokens received
         */
        struct Supply {
            address supplier;
            bytes32 partition;
            uint256 amount;
        }
    
        /**********************************************************************************************
         * Supply State
         *********************************************************************************************/
    
        /**
         * @notice Supply nonce used to track incoming token transfers
         */
        uint256 public supplyNonce = 0;
    
        /**
         * @notice Mapping of all incoming token transfers
         */
        mapping(uint256 => Supply) public nonceToSupply;
    
        /**********************************************************************************************
         * Withdrawal State
         *********************************************************************************************/
    
        /**
         * @notice Remaining withdrawal limit. Initially set to 100,000 Amp.
         */
        uint256 public withdrawalLimit = 100 * 1000 * (10**18);
    
        /**
         * @notice Withdrawal maximum root nonce
         */
        uint256 public maxWithdrawalRootNonce = 0;
    
        /**
         * @notice Active set of withdrawal roots
         */
        mapping(bytes32 => uint256) public withdrawalRootToNonce;
    
        /**
         * @notice Last invoked withdrawal root for each account, per partition
         */
        mapping(bytes32 => mapping(address => uint256)) public addressToWithdrawalNonce;
    
        /**
         * @notice Total amount withdrawn for each account, per partition
         */
        mapping(bytes32 => mapping(address => uint256)) public addressToCumulativeAmountWithdrawn;
    
        /**********************************************************************************************
         * Fallback Withdrawal State
         *********************************************************************************************/
    
        /**
         * @notice Withdrawal fallback delay. Initially set to one week.
         */
        uint256 public fallbackWithdrawalDelaySeconds = 1 weeks;
    
        /**
         * @notice Current fallback withdrawal root
         */
        bytes32 public fallbackRoot;
    
        /**
         * @notice Timestamp of when the last fallback root was published
         */
        uint256 public fallbackSetDate = 2**200; // very far in the future
    
        /**
         * @notice Latest supply reflected in the fallback withdrawal authorization tree
         */
        uint256 public fallbackMaxIncludedSupplyNonce = 0;
    
        /**********************************************************************************************
         * Supplier Events
         *********************************************************************************************/
    
        /**
         * @notice Indicates a token supply has been received
         * @param supplier Token supplier
         * @param amount Number of tokens transferred
         * @param nonce Nonce of the supply
         */
        event SupplyReceipt(
            address indexed supplier,
            bytes32 indexed partition,
            uint256 amount,
            uint256 indexed nonce
        );
    
        /**
         * @notice Indicates that a withdrawal was executed
         * @param supplier Address whose withdrawal authorization was executed
         * @param partition Partition from which the tokens were transferred
         * @param amount Amount of tokens transferred
         * @param rootNonce Nonce of the withdrawal root used for authorization
         * @param authorizedAccountNonce Maximum previous nonce used by the account
         */
        event Withdrawal(
            address indexed supplier,
            bytes32 indexed partition,
            uint256 amount,
            uint256 indexed rootNonce,
            uint256 authorizedAccountNonce
        );
    
        /**
         * @notice Indicates a fallback withdrawal was executed
         * @param supplier Address whose fallback withdrawal authorization was executed
         * @param partition Partition from which the tokens were transferred
         * @param amount Amount of tokens transferred
         */
        event FallbackWithdrawal(
            address indexed supplier,
            bytes32 indexed partition,
            uint256 indexed amount
        );
    
        /**
         * @notice Indicates a release of supply is requested
         * @param supplier Token supplier
         * @param partition Parition from which the tokens should be released
         * @param amount Number of tokens requested to be released
         * @param data Metadata provided by the requestor
         */
        event ReleaseRequest(
            address indexed supplier,
            bytes32 indexed partition,
            uint256 indexed amount,
            bytes data
        );
    
        /**
         * @notice Indicates a supply refund was executed
         * @param supplier Address whose refund authorization was executed
         * @param partition Partition from which the tokens were transferred
         * @param amount Amount of tokens transferred
         * @param nonce Nonce of the original supply
         */
        event SupplyRefund(
            address indexed supplier,
            bytes32 indexed partition,
            uint256 amount,
            uint256 indexed nonce
        );
    
        /**********************************************************************************************
         * Direct Transfer Events
         *********************************************************************************************/
    
        /**
         * @notice Emitted when tokens are directly transfered
         * @param operator Address that executed the direct transfer
         * @param from_partition Partition from which the tokens were transferred
         * @param to_address Address to which the tokens were transferred
         * @param to_partition Partition to which the tokens were transferred
         * @param value Amount of tokens transferred
         */
        event DirectTransfer(
            address operator,
            bytes32 indexed from_partition,
            address indexed to_address,
            bytes32 indexed to_partition,
            uint256 value
        );
    
        /**********************************************************************************************
         * Admin Configuration Events
         *********************************************************************************************/
    
        /**
         * @notice Emitted when a partition is permitted for supply
         * @param partition Partition added to the permitted set
         */
        event PartitionAdded(bytes32 indexed partition);
    
        /**
         * @notice Emitted when a partition is removed from the set permitted for supply
         * @param partition Partition removed from the permitted set
         */
        event PartitionRemoved(bytes32 indexed partition);
    
        /**********************************************************************************************
         * Admin Withdrawal Management Events
         *********************************************************************************************/
    
        /**
         * @notice Emitted when a new withdrawal root hash is added to the active set
         * @param rootHash Merkle root hash.
         * @param nonce Nonce of the Merkle root hash.
         */
        event WithdrawalRootHashAddition(bytes32 indexed rootHash, uint256 indexed nonce);
    
        /**
         * @notice Emitted when a withdrawal root hash is removed from the active set
         * @param rootHash Merkle root hash.
         * @param nonce Nonce of the Merkle root hash.
         */
        event WithdrawalRootHashRemoval(bytes32 indexed rootHash, uint256 indexed nonce);
    
        /**
         * @notice Emitted when the withdrawal limit is updated
         * @param oldValue Old limit.
         * @param newValue New limit.
         */
        event WithdrawalLimitUpdate(uint256 indexed oldValue, uint256 indexed newValue);
    
        /**********************************************************************************************
         * Admin Fallback Management Events
         *********************************************************************************************/
    
        /**
         * @notice Emitted when a new fallback withdrawal root hash is set
         * @param rootHash Merkle root hash
         * @param maxSupplyNonceIncluded Nonce of the last supply reflected in the tree data
         * @param setDate Timestamp of when the root hash was set
         */
        event FallbackRootHashSet(
            bytes32 indexed rootHash,
            uint256 indexed maxSupplyNonceIncluded,
            uint256 setDate
        );
    
        /**
         * @notice Emitted when the fallback root hash set date is reset
         * @param newDate Timestamp of when the fallback reset date was set
         */
        event FallbackMechanismDateReset(uint256 indexed newDate);
    
        /**
         * @notice Emitted when the fallback delay is updated
         * @param oldValue Old delay
         * @param newValue New delay
         */
        event FallbackWithdrawalDelayUpdate(uint256 indexed oldValue, uint256 indexed newValue);
    
        /**********************************************************************************************
         * Role Management Events
         *********************************************************************************************/
    
        /**
         * @notice Emitted when the Withdrawal Publisher is updated
         * @param oldValue Old publisher
         * @param newValue New publisher
         */
        event WithdrawalPublisherUpdate(address indexed oldValue, address indexed newValue);
    
        /**
         * @notice Emitted when the Fallback Publisher is updated
         * @param oldValue Old publisher
         * @param newValue New publisher
         */
        event FallbackPublisherUpdate(address indexed oldValue, address indexed newValue);
    
        /**
         * @notice Emitted when Withdrawal Limit Publisher is updated
         * @param oldValue Old publisher
         * @param newValue New publisher
         */
        event WithdrawalLimitPublisherUpdate(address indexed oldValue, address indexed newValue);
    
        /**
         * @notice Emitted when the DirectTransferer address is updated
         * @param oldValue Old DirectTransferer address
         * @param newValue New DirectTransferer address
         */
        event DirectTransfererUpdate(address indexed oldValue, address indexed newValue);
    
        /**
         * @notice Emitted when the Partition Manager address is updated
         * @param oldValue Old Partition Manager address
         * @param newValue New Partition Manager address
         */
        event PartitionManagerUpdate(address indexed oldValue, address indexed newValue);
    
        /**********************************************************************************************
         * Constructor
         *********************************************************************************************/
    
        /**
         * @notice FlexaCollateralManager constructor
         * @param _amp Address of the Amp token contract
         */
        constructor(address _amp) public {
            amp = _amp;
    
            ERC1820Client.setInterfaceImplementation(AMP_TOKENS_RECIPIENT, address(this));
            ERC1820Client.setInterfaceImplementation(AMP_TOKENS_SENDER, address(this));
    
            IAmp(amp).registerCollateralManager();
        }
    
        /**********************************************************************************************
         * IAmpTokensRecipient Hooks
         *********************************************************************************************/
    
        /**
         * @notice Validates where the supplied parameters are valid for a transfer of tokens to this
         * contract
         * @dev Implements IAmpTokensRecipient
         * @param _partition Partition from which the tokens were transferred
         * @param _to The destination address of the tokens. Must be this.
         * @param _data Optional data sent with the transfer. Used to set the destination partition.
         * @return true if the tokens can be received, otherwise false
         */
        function canReceive(
            bytes4, /* functionSig */
            bytes32 _partition,
            address, /* operator */
            address, /* from */
            address _to,
            uint256, /* value */
            bytes calldata _data,
            bytes calldata /* operatorData */
        ) external override view returns (bool) {
            if (msg.sender != amp || _to != address(this)) {
                return false;
            }
    
            bytes32 _destinationPartition = PartitionUtils._getDestinationPartition(_data, _partition);
    
            return partitions[_destinationPartition];
        }
    
        /**
         * @notice Function called by the token contract after executing a transfer.
         * @dev Implements IAmpTokensRecipient
         * @param _partition Partition from which the tokens were transferred
         * @param _operator Address which triggered the transfer. This address will be credited with
         * the supply.
         * @param _to The destination address of the tokens. Must be this.
         * @param _value Number of tokens the token holder balance is decreased by.
         * @param _data Optional data sent with the transfer. Used to set the destination partition.
         */
        function tokensReceived(
            bytes4, /* functionSig */
            bytes32 _partition,
            address _operator,
            address, /* from */
            address _to,
            uint256 _value,
            bytes calldata _data,
            bytes calldata /* operatorData */
        ) external override {
            require(msg.sender == amp, "Invalid sender");
            require(_to == address(this), "Invalid to address");
    
            bytes32 _destinationPartition = PartitionUtils._getDestinationPartition(_data, _partition);
    
            require(partitions[_destinationPartition], "Invalid destination partition");
    
            supplyNonce = SafeMath.add(supplyNonce, 1);
            nonceToSupply[supplyNonce].supplier = _operator;
            nonceToSupply[supplyNonce].partition = _destinationPartition;
            nonceToSupply[supplyNonce].amount = _value;
    
            emit SupplyReceipt(_operator, _destinationPartition, _value, supplyNonce);
        }
    
        /**********************************************************************************************
         * IAmpTokensSender Hooks
         *********************************************************************************************/
    
        /**
         * @notice Validates where the supplied parameters are valid for a transfer of tokens from this
         * contract
         * @dev Implements IAmpTokensSender
         * @param _partition Source partition of the tokens
         * @param _operator Address which triggered the transfer
         * @param _from The source address of the tokens. Must be this.
         * @param _value Amount of tokens to be transferred
         * @param _operatorData Extra information attached by the operator. Must include the transfer
         * operation flag and additional authorization data custom for each transfer operation type.
         * @return true if the token transfer would succeed, otherwise false
         */
        function canTransfer(
            bytes4, /*functionSig*/
            bytes32 _partition,
            address _operator,
            address _from,
            address, /* to */
            uint256 _value,
            bytes calldata, /* data */
            bytes calldata _operatorData
        ) external override view returns (bool) {
            if (msg.sender != amp || _from != address(this)) {
                return false;
            }
    
            bytes32 flag = _decodeOperatorDataFlag(_operatorData);
    
            if (flag == WITHDRAWAL_FLAG) {
                return _validateWithdrawal(_partition, _operator, _value, _operatorData);
            }
            if (flag == FALLBACK_WITHDRAWAL_FLAG) {
                return _validateFallbackWithdrawal(_partition, _operator, _value, _operatorData);
            }
            if (flag == REFUND_FLAG) {
                return _validateRefund(_partition, _operator, _value, _operatorData);
            }
            if (flag == DIRECT_TRANSFER_FLAG) {
                return _validateDirectTransfer(_operator, _value);
            }
    
            return false;
        }
    
        /**
         * @notice Function called by the token contract when executing a transfer
         * @dev Implements IAmpTokensSender
         * @param _partition Source partition of the tokens
         * @param _operator Address which triggered the transfer
         * @param _from The source address of the tokens. Must be this.
         * @param _to The target address of the tokens.
         * @param _value Amount of tokens to be transferred
         * @param _data Data attached to the transfer. Typically includes partition change information.
         * @param _operatorData Extra information attached by the operator. Must include the transfer
         * operation flag and additional authorization data custom for each transfer operation type.
         */
        function tokensToTransfer(
            bytes4, /* functionSig */
            bytes32 _partition,
            address _operator,
            address _from,
            address _to,
            uint256 _value,
            bytes calldata _data,
            bytes calldata _operatorData
        ) external override {
            require(msg.sender == amp, "Invalid sender");
            require(_from == address(this), "Invalid from address");
    
            bytes32 flag = _decodeOperatorDataFlag(_operatorData);
    
            if (flag == WITHDRAWAL_FLAG) {
                _executeWithdrawal(_partition, _operator, _value, _operatorData);
            } else if (flag == FALLBACK_WITHDRAWAL_FLAG) {
                _executeFallbackWithdrawal(_partition, _operator, _value, _operatorData);
            } else if (flag == REFUND_FLAG) {
                _executeRefund(_partition, _operator, _value, _operatorData);
            } else if (flag == DIRECT_TRANSFER_FLAG) {
                _executeDirectTransfer(_partition, _operator, _to, _value, _data);
            } else {
                revert("invalid flag");
            }
        }
    
        /**********************************************************************************************
         * Withdrawals
         *********************************************************************************************/
    
        /**
         * @notice Validates withdrawal data
         * @param _partition Source partition of the withdrawal
         * @param _operator Address that is invoking the transfer
         * @param _value Number of tokens to be transferred
         * @param _operatorData Contains the withdrawal authorization data
         * @return true if the withdrawal data is valid, otherwise false
         */
        function _validateWithdrawal(
            bytes32 _partition,
            address _operator,
            uint256 _value,
            bytes memory _operatorData
        ) internal view returns (bool) {
            (
                address supplier,
                uint256 maxAuthorizedAccountNonce,
                uint256 withdrawalRootNonce
            ) = _getWithdrawalData(_partition, _value, _operatorData);
    
            return
                _validateWithdrawalData(
                    _partition,
                    _operator,
                    _value,
                    supplier,
                    maxAuthorizedAccountNonce,
                    withdrawalRootNonce
                );
        }
    
        /**
         * @notice Validates the withdrawal data and updates state to reflect the transfer
         * @param _partition Source partition of the withdrawal
         * @param _operator Address that is invoking the transfer
         * @param _value Number of tokens to be transferred
         * @param _operatorData Contains the withdrawal authorization data
         */
        function _executeWithdrawal(
            bytes32 _partition,
            address _operator,
            uint256 _value,
            bytes memory _operatorData
        ) internal {
            (
                address supplier,
                uint256 maxAuthorizedAccountNonce,
                uint256 withdrawalRootNonce
            ) = _getWithdrawalData(_partition, _value, _operatorData);
    
            require(
                _validateWithdrawalData(
                    _partition,
                    _operator,
                    _value,
                    supplier,
                    maxAuthorizedAccountNonce,
                    withdrawalRootNonce
                ),
                "Transfer unauthorized"
            );
    
            addressToCumulativeAmountWithdrawn[_partition][supplier] = SafeMath.add(
                _value,
                addressToCumulativeAmountWithdrawn[_partition][supplier]
            );
    
            addressToWithdrawalNonce[_partition][supplier] = withdrawalRootNonce;
    
            withdrawalLimit = SafeMath.sub(withdrawalLimit, _value);
    
            emit Withdrawal(
                supplier,
                _partition,
                _value,
                withdrawalRootNonce,
                maxAuthorizedAccountNonce
            );
        }
    
        /**
         * @notice Extracts withdrawal data from the supplied parameters
         * @param _partition Source partition of the withdrawal
         * @param _value Number of tokens to be transferred
         * @param _operatorData Contains the withdrawal authorization data, including the withdrawal
         * operation flag, supplier, maximum authorized account nonce, and Merkle proof.
         * @return supplier, the address whose account is authorized
         * @return maxAuthorizedAccountNonce, the maximum existing used withdrawal nonce for the
         * supplier and partition
         * @return withdrawalRootNonce, the active withdrawal root nonce found based on the supplied
         * data and Merkle proof
         */
        function _getWithdrawalData(
            bytes32 _partition,
            uint256 _value,
            bytes memory _operatorData
        )
            internal
            view
            returns (
                address, /* supplier */
                uint256, /* maxAuthorizedAccountNonce */
                uint256 /* withdrawalRootNonce */
            )
        {
            (
                address supplier,
                uint256 maxAuthorizedAccountNonce,
                bytes32[] memory merkleProof
            ) = _decodeWithdrawalOperatorData(_operatorData);
    
            bytes32 leafDataHash = _calculateWithdrawalLeaf(
                supplier,
                _partition,
                _value,
                maxAuthorizedAccountNonce
            );
    
            bytes32 calculatedRoot = _calculateMerkleRoot(merkleProof, leafDataHash);
            uint256 withdrawalRootNonce = withdrawalRootToNonce[calculatedRoot];
    
            return (supplier, maxAuthorizedAccountNonce, withdrawalRootNonce);
        }
    
        /**
         * @notice Validates that the parameters are valid for the requested withdrawal
         * @param _partition Source partition of the tokens
         * @param _operator Address that is executing the withdrawal
         * @param _value Number of tokens to be transferred
         * @param _supplier The address whose account is authorized
         * @param _maxAuthorizedAccountNonce The maximum existing used withdrawal nonce for the
         * supplier and partition
         * @param _withdrawalRootNonce The active withdrawal root nonce found based on the supplied
         * data and Merkle proof
         * @return true if the withdrawal data is valid, otherwise false
         */
        function _validateWithdrawalData(
            bytes32 _partition,
            address _operator,
            uint256 _value,
            address _supplier,
            uint256 _maxAuthorizedAccountNonce,
            uint256 _withdrawalRootNonce
        ) internal view returns (bool) {
            return
                // Only owner, withdrawal publisher or supplier can invoke withdrawals
                (_operator == owner() || _operator == withdrawalPublisher || _operator == _supplier) &&
                // Ensure maxAuthorizedAccountNonce has not been exceeded
                (addressToWithdrawalNonce[_partition][_supplier] <= _maxAuthorizedAccountNonce) &&
                // Ensure we are within the global withdrawal limit
                (_value <= withdrawalLimit) &&
                // Merkle tree proof is valid
                (_withdrawalRootNonce > 0) &&
                // Ensure the withdrawal root is more recent than the maxAuthorizedAccountNonce
                (_withdrawalRootNonce > _maxAuthorizedAccountNonce);
        }
    
        /**********************************************************************************************
         * Fallback Withdrawals
         *********************************************************************************************/
    
        /**
         * @notice Validates fallback withdrawal data
         * @param _partition Source partition of the withdrawal
         * @param _operator Address that is invoking the transfer
         * @param _value Number of tokens to be transferred
         * @param _operatorData Contains the fallback withdrawal authorization data
         * @return true if the fallback withdrawal data is valid, otherwise false
         */
        function _validateFallbackWithdrawal(
            bytes32 _partition,
            address _operator,
            uint256 _value,
            bytes memory _operatorData
        ) internal view returns (bool) {
            (
                address supplier,
                uint256 maxCumulativeWithdrawalAmount,
                uint256 newCumulativeWithdrawalAmount,
                bytes32 calculatedRoot
            ) = _getFallbackWithdrawalData(_partition, _value, _operatorData);
    
            return
                _validateFallbackWithdrawalData(
                    _operator,
                    maxCumulativeWithdrawalAmount,
                    newCumulativeWithdrawalAmount,
                    supplier,
                    calculatedRoot
                );
        }
    
        /**
         * @notice Validates the fallback withdrawal data and updates state to reflect the transfer
         * @param _partition Source partition of the withdrawal
         * @param _operator Address that is invoking the transfer
         * @param _value Number of tokens to be transferred
         * @param _operatorData Contains the fallback withdrawal authorization data
         */
        function _executeFallbackWithdrawal(
            bytes32 _partition,
            address _operator,
            uint256 _value,
            bytes memory _operatorData
        ) internal {
            (
                address supplier,
                uint256 maxCumulativeWithdrawalAmount,
                uint256 newCumulativeWithdrawalAmount,
                bytes32 calculatedRoot
            ) = _getFallbackWithdrawalData(_partition, _value, _operatorData);
    
            require(
                _validateFallbackWithdrawalData(
                    _operator,
                    maxCumulativeWithdrawalAmount,
                    newCumulativeWithdrawalAmount,
                    supplier,
                    calculatedRoot
                ),
                "Transfer unauthorized"
            );
    
            addressToCumulativeAmountWithdrawn[_partition][supplier] = newCumulativeWithdrawalAmount;
    
            addressToWithdrawalNonce[_partition][supplier] = maxWithdrawalRootNonce;
    
            emit FallbackWithdrawal(supplier, _partition, _value);
        }
    
        /**
         * @notice Extracts withdrawal data from the supplied parameters
         * @param _partition Source partition of the withdrawal
         * @param _value Number of tokens to be transferred
         * @param _operatorData Contains the fallback withdrawal authorization data, including the
         * fallback withdrawal operation flag, supplier, max cumulative withdrawal amount, and Merkle
         * proof.
         * @return supplier, the address whose account is authorized
         * @return maxCumulativeWithdrawalAmount, the maximum amount of tokens that can be withdrawn
         * for the supplier's account, including both withdrawals and fallback withdrawals
         * @return newCumulativeWithdrawalAmount, the new total of all withdrawals include the
         * current request
         * @return calculatedRoot, the Merkle tree root calculated based on the supplied data and proof
         */
        function _getFallbackWithdrawalData(
            bytes32 _partition,
            uint256 _value,
            bytes memory _operatorData
        )
            internal
            view
            returns (
                address, /* supplier */
                uint256, /* maxCumulativeWithdrawalAmount */
                uint256, /* newCumulativeWithdrawalAmount */
                bytes32 /* calculatedRoot */
            )
        {
            (
                address supplier,
                uint256 maxCumulativeWithdrawalAmount,
                bytes32[] memory merkleProof
            ) = _decodeWithdrawalOperatorData(_operatorData);
    
            uint256 newCumulativeWithdrawalAmount = SafeMath.add(
                _value,
                addressToCumulativeAmountWithdrawn[_partition][supplier]
            );
    
            bytes32 leafDataHash = _calculateFallbackLeaf(
                supplier,
                _partition,
                maxCumulativeWithdrawalAmount
            );
            bytes32 calculatedRoot = _calculateMerkleRoot(merkleProof, leafDataHash);
    
            return (
                supplier,
                maxCumulativeWithdrawalAmount,
                newCumulativeWithdrawalAmount,
                calculatedRoot
            );
        }
    
        /**
         * @notice Validates that the parameters are valid for the requested fallback withdrawal
         * @param _operator Address that is executing the withdrawal
         * @param _maxCumulativeWithdrawalAmount, the maximum amount of tokens that can be withdrawn
         * for the supplier's account, including both withdrawals and fallback withdrawals
         * @param _newCumulativeWithdrawalAmount, the new total of all withdrawals include the
         * current request
         * @param _supplier The address whose account is authorized
         * @param _calculatedRoot The Merkle tree root calculated based on the supplied data and proof
         * @return true if the fallback withdrawal data is valid, otherwise false
         */
        function _validateFallbackWithdrawalData(
            address _operator,
            uint256 _maxCumulativeWithdrawalAmount,
            uint256 _newCumulativeWithdrawalAmount,
            address _supplier,
            bytes32 _calculatedRoot
        ) internal view returns (bool) {
            return
                // Only owner or supplier can invoke the fallback withdrawal
                (_operator == owner() || _operator == _supplier) &&
                // Ensure we have entered fallback mode
                (SafeMath.add(fallbackSetDate, fallbackWithdrawalDelaySeconds) <= block.timestamp) &&
                // Check that the maximum allowable withdrawal for the supplier has not been exceeded
                (_newCumulativeWithdrawalAmount <= _maxCumulativeWithdrawalAmount) &&
                // Merkle tree proof is valid
                (fallbackRoot == _calculatedRoot);
        }
    
        /**********************************************************************************************
         * Supply Refunds
         *********************************************************************************************/
    
        /**
         * @notice Validates refund data
         * @param _partition Source partition of the refund
         * @param _operator Address that is invoking the transfer
         * @param _value Number of tokens to be transferred
         * @param _operatorData Contains the refund authorization data
         * @return true if the refund data is valid, otherwise false
         */
        function _validateRefund(
            bytes32 _partition,
            address _operator,
            uint256 _value,
            bytes memory _operatorData
        ) internal view returns (bool) {
            (uint256 _supplyNonce, Supply memory supply) = _getRefundData(_operatorData);
    
            return _verifyRefundData(_partition, _operator, _value, _supplyNonce, supply);
        }
    
        /**
         * @notice Validates the refund data and updates state to reflect the transfer
         * @param _partition Source partition of the refund
         * @param _operator Address that is invoking the transfer
         * @param _value Number of tokens to be transferred
         * @param _operatorData Contains the refund authorization data
         */
        function _executeRefund(
            bytes32 _partition,
            address _operator,
            uint256 _value,
            bytes memory _operatorData
        ) internal {
            (uint256 nonce, Supply memory supply) = _getRefundData(_operatorData);
    
            require(
                _verifyRefundData(_partition, _operator, _value, nonce, supply),
                "Transfer unauthorized"
            );
    
            delete nonceToSupply[nonce];
    
            emit SupplyRefund(supply.supplier, _partition, supply.amount, nonce);
        }
    
        /**
         * @notice Extracts refund data from the supplied parameters
         * @param _operatorData Contains the refund authorization data, including the refund
         * operation flag and supply nonce.
         * @return supplyNonce, nonce of the recorded supply
         * @return supply, The supplier, partition and amount of tokens in the original supply
         */
        function _getRefundData(bytes memory _operatorData)
            internal
            view
            returns (uint256, Supply memory)
        {
            uint256 _supplyNonce = _decodeRefundOperatorData(_operatorData);
            Supply memory supply = nonceToSupply[_supplyNonce];
    
            return (_supplyNonce, supply);
        }
    
        /**
         * @notice Validates that the parameters are valid for the requested refund
         * @param _partition Source partition of the tokens
         * @param _operator Address that is executing the refund
         * @param _value Number of tokens to be transferred
         * @param _supplyNonce nonce of the recorded supply
         * @param _supply The supplier, partition and amount of tokens in the original supply
         * @return true if the refund data is valid, otherwise false
         */
        function _verifyRefundData(
            bytes32 _partition,
            address _operator,
            uint256 _value,
            uint256 _supplyNonce,
            Supply memory _supply
        ) internal view returns (bool) {
            return
                // Supply record exists
                (_supply.amount > 0) &&
                // Only owner or supplier can invoke the refund
                (_operator == owner() || _operator == _supply.supplier) &&
                // Requested partition matches the Supply record
                (_partition == _supply.partition) &&
                // Requested value matches the Supply record
                (_value == _supply.amount) &&
                // Ensure we have entered fallback mode
                (SafeMath.add(fallbackSetDate, fallbackWithdrawalDelaySeconds) <= block.timestamp) &&
                // Supply has not already been included in the fallback withdrawal data
                (_supplyNonce > fallbackMaxIncludedSupplyNonce);
        }
    
        /**********************************************************************************************
         * Direct Transfers
         *********************************************************************************************/
    
        /**
         * @notice Validates direct transfer data
         * @param _operator Address that is invoking the transfer
         * @param _value Number of tokens to be transferred
         * @return true if the direct transfer data is valid, otherwise false
         */
        function _validateDirectTransfer(address _operator, uint256 _value)
            internal
            view
            returns (bool)
        {
            return
                // Only owner and directTransferer can invoke withdrawals
                (_operator == owner() || _operator == directTransferer) &&
                // Ensure we are within the global withdrawal limit
                (_value <= withdrawalLimit);
        }
    
        /**
         * @notice Validates the direct transfer data and updates state to reflect the transfer
         * @param _partition Source partition of the direct transfer
         * @param _operator Address that is invoking the transfer
         * @param _to The target address of the tokens.
         * @param _value Number of tokens to be transferred
         * @param _data Data attached to the transfer. Typically includes partition change information.
         */
        function _executeDirectTransfer(
            bytes32 _partition,
            address _operator,
            address _to,
            uint256 _value,
            bytes memory _data
        ) internal {
            require(_validateDirectTransfer(_operator, _value), "Transfer unauthorized");
    
            withdrawalLimit = SafeMath.sub(withdrawalLimit, _value);
    
            bytes32 to_partition = PartitionUtils._getDestinationPartition(_data, _partition);
    
            emit DirectTransfer(_operator, _partition, _to, to_partition, _value);
        }
    
        /**********************************************************************************************
         * Release Request
         *********************************************************************************************/
    
        /**
         * @notice Emits a release request event that can be used to trigger the release of tokens
         * @param _partition Parition from which the tokens should be released
         * @param _amount Number of tokens requested to be released
         * @param _data Metadata to include with the release request
         */
        function requestRelease(
            bytes32 _partition,
            uint256 _amount,
            bytes memory _data
        ) external {
            emit ReleaseRequest(msg.sender, _partition, _amount, _data);
        }
    
        /**********************************************************************************************
         * Partition Management
         *********************************************************************************************/
    
        /**
         * @notice Adds a partition to the set allowed to receive tokens
         * @param _partition Parition to be permitted for incoming transfers
         */
        function addPartition(bytes32 _partition) external {
            require(msg.sender == owner() || msg.sender == partitionManager, "Invalid sender");
            require(partitions[_partition] == false, "Partition already permitted");
    
            (bytes4 prefix, , address partitionOwner) = PartitionUtils._splitPartition(_partition);
    
            require(prefix == PARTITION_PREFIX, "Invalid partition prefix");
            require(partitionOwner == address(this), "Invalid partition owner");
    
            partitions[_partition] = true;
    
            emit PartitionAdded(_partition);
        }
    
        /**
         * @notice Removes a partition from the set allowed to receive tokens
         * @param _partition Parition to be disallowed from incoming transfers
         */
        function removePartition(bytes32 _partition) external {
            require(msg.sender == owner() || msg.sender == partitionManager, "Invalid sender");
            require(partitions[_partition], "Partition not permitted");
    
            delete partitions[_partition];
    
            emit PartitionRemoved(_partition);
        }
    
        /**********************************************************************************************
         * Withdrawal Management
         *********************************************************************************************/
    
        /**
         * @notice Modifies the withdrawal limit by the provided amount.
         * @param _amount Limit delta
         */
        function modifyWithdrawalLimit(int256 _amount) external {
            require(msg.sender == owner() || msg.sender == withdrawalLimitPublisher, "Invalid sender");
            uint256 oldLimit = withdrawalLimit;
            if (_amount < 0) {
                uint256 unsignedAmount = uint256(-_amount);
                withdrawalLimit = SafeMath.sub(withdrawalLimit, unsignedAmount);
            } else {
                uint256 unsignedAmount = uint256(_amount);
                withdrawalLimit = SafeMath.add(withdrawalLimit, unsignedAmount);
            }
            emit WithdrawalLimitUpdate(oldLimit, withdrawalLimit);
        }
    
        /**
         * @notice Adds the root hash of a Merkle tree containing authorized token withdrawals to the
         * active set
         * @param _root The root hash to be added to the active set
         * @param _nonce The nonce of the new root hash. Must be exactly one higher than the existing
         * max nonce.
         * @param _replacedRoots The root hashes to be removed from the repository.
         */
        function addWithdrawalRoot(
            bytes32 _root,
            uint256 _nonce,
            bytes32[] calldata _replacedRoots
        ) external {
            require(msg.sender == owner() || msg.sender == withdrawalPublisher, "Invalid sender");
    
            require(_root != 0, "Invalid root");
            require(maxWithdrawalRootNonce + 1 == _nonce, "Nonce not current max plus one");
            require(withdrawalRootToNonce[_root] == 0, "Nonce already used");
    
            withdrawalRootToNonce[_root] = _nonce;
            maxWithdrawalRootNonce = _nonce;
    
            emit WithdrawalRootHashAddition(_root, _nonce);
    
            for (uint256 i = 0; i < _replacedRoots.length; i++) {
                deleteWithdrawalRoot(_replacedRoots[i]);
            }
        }
    
        /**
         * @notice Removes withdrawal root hashes from active set
         * @param _roots The root hashes to be removed from the active set
         */
        function removeWithdrawalRoots(bytes32[] calldata _roots) external {
            require(msg.sender == owner() || msg.sender == withdrawalPublisher, "Invalid sender");
    
            for (uint256 i = 0; i < _roots.length; i++) {
                deleteWithdrawalRoot(_roots[i]);
            }
        }
    
        /**
         * @notice Removes a withdrawal root hash from active set
         * @param _root The root hash to be removed from the active set
         */
        function deleteWithdrawalRoot(bytes32 _root) private {
            uint256 nonce = withdrawalRootToNonce[_root];
    
            require(nonce > 0, "Root not found");
    
            delete withdrawalRootToNonce[_root];
    
            emit WithdrawalRootHashRemoval(_root, nonce);
        }
    
        /**********************************************************************************************
         * Fallback Management
         *********************************************************************************************/
    
        /**
         * @notice Sets the root hash of the Merkle tree containing fallback
         * withdrawal authorizations.
         * @param _root The root hash of a Merkle tree containing the fallback withdrawal
         * authorizations
         * @param _maxSupplyNonce The nonce of the latest supply whose value is reflected in the
         * fallback withdrawal authorizations.
         */
        function setFallbackRoot(bytes32 _root, uint256 _maxSupplyNonce) external {
            require(msg.sender == owner() || msg.sender == fallbackPublisher, "Invalid sender");
            require(_root != 0, "Invalid root");
            require(
                SafeMath.add(fallbackSetDate, fallbackWithdrawalDelaySeconds) > block.timestamp,
                "Fallback is active"
            );
            require(
                _maxSupplyNonce >= fallbackMaxIncludedSupplyNonce,
                "Included supply nonce decreased"
            );
            require(_maxSupplyNonce <= supplyNonce, "Included supply nonce exceeds latest supply");
    
            fallbackRoot = _root;
            fallbackMaxIncludedSupplyNonce = _maxSupplyNonce;
            fallbackSetDate = block.timestamp;
    
            emit FallbackRootHashSet(_root, fallbackMaxIncludedSupplyNonce, block.timestamp);
        }
    
        /**
         * @notice Resets the fallback set date to the current block's timestamp. This can be used to
         * delay the start of the fallback period without publishing a new root, or to deactivate the
         * fallback mechanism so a new fallback root may be published.
         */
        function resetFallbackMechanismDate() external {
            require(msg.sender == owner() || msg.sender == fallbackPublisher, "Invalid sender");
            fallbackSetDate = block.timestamp;
    
            emit FallbackMechanismDateReset(fallbackSetDate);
        }
    
        /**
         * @notice Updates the time-lock period before the fallback mechanism is activated after the
         * last fallback root was published.
         * @param _newFallbackDelaySeconds The new delay period in seconds
         */
        function setFallbackWithdrawalDelay(uint256 _newFallbackDelaySeconds) external {
            require(msg.sender == owner(), "Invalid sender");
            require(_newFallbackDelaySeconds != 0, "Invalid zero delay seconds");
            require(_newFallbackDelaySeconds < 10 * 365 days, "Invalid delay over 10 years");
    
            uint256 oldDelay = fallbackWithdrawalDelaySeconds;
            fallbackWithdrawalDelaySeconds = _newFallbackDelaySeconds;
    
            emit FallbackWithdrawalDelayUpdate(oldDelay, _newFallbackDelaySeconds);
        }
    
        /**********************************************************************************************
         * Role Management
         *********************************************************************************************/
    
        /**
         * @notice Updates the Withdrawal Publisher address, the only address other than the owner that
         * can publish / remove withdrawal Merkle tree roots.
         * @param _newWithdrawalPublisher The address of the new Withdrawal Publisher
         * @dev Error invalid sender.
         */
        function setWithdrawalPublisher(address _newWithdrawalPublisher) external {
            require(msg.sender == owner(), "Invalid sender");
    
            address oldValue = withdrawalPublisher;
            withdrawalPublisher = _newWithdrawalPublisher;
    
            emit WithdrawalPublisherUpdate(oldValue, withdrawalPublisher);
        }
    
        /**
         * @notice Updates the Fallback Publisher address, the only address other than the owner that
         * can publish / remove fallback withdrawal Merkle tree roots.
         * @param _newFallbackPublisher The address of the new Fallback Publisher
         * @dev Error invalid sender.
         */
        function setFallbackPublisher(address _newFallbackPublisher) external {
            require(msg.sender == owner(), "Invalid sender");
    
            address oldValue = fallbackPublisher;
            fallbackPublisher = _newFallbackPublisher;
    
            emit FallbackPublisherUpdate(oldValue, fallbackPublisher);
        }
    
        /**
         * @notice Updates the Withdrawal Limit Publisher address, the only address other than the
         * owner that can set the withdrawal limit.
         * @param _newWithdrawalLimitPublisher The address of the new Withdrawal Limit Publisher
         * @dev Error invalid sender.
         */
        function setWithdrawalLimitPublisher(address _newWithdrawalLimitPublisher) external {
            require(msg.sender == owner(), "Invalid sender");
    
            address oldValue = withdrawalLimitPublisher;
            withdrawalLimitPublisher = _newWithdrawalLimitPublisher;
    
            emit WithdrawalLimitPublisherUpdate(oldValue, withdrawalLimitPublisher);
        }
    
        /**
         * @notice Updates the DirectTransferer address, the only address other than the owner that
         * can execute direct transfers
         * @param _newDirectTransferer The address of the new DirectTransferer
         */
        function setDirectTransferer(address _newDirectTransferer) external {
            require(msg.sender == owner(), "Invalid sender");
    
            address oldValue = directTransferer;
            directTransferer = _newDirectTransferer;
    
            emit DirectTransfererUpdate(oldValue, directTransferer);
        }
    
        /**
         * @notice Updates the Partition Manager address, the only address other than the owner that
         * can add and remove permitted partitions
         * @param _newPartitionManager The address of the new PartitionManager
         */
        function setPartitionManager(address _newPartitionManager) external {
            require(msg.sender == owner(), "Invalid sender");
    
            address oldValue = partitionManager;
            partitionManager = _newPartitionManager;
    
            emit PartitionManagerUpdate(oldValue, partitionManager);
        }
    
        /**********************************************************************************************
         * Operator Data Decoders
         *********************************************************************************************/
    
        /**
         * @notice Extract flag from operatorData
         * @param _operatorData The operator data to be decoded
         * @return flag, the transfer operation type
         */
        function _decodeOperatorDataFlag(bytes memory _operatorData) internal pure returns (bytes32) {
            return abi.decode(_operatorData, (bytes32));
        }
    
        /**
         * @notice Extracts the supplier, max authorized nonce, and Merkle proof from the operator data
         * @param _operatorData The operator data to be decoded
         * @return supplier, the address whose account is authorized
         * @return For withdrawals: max authorized nonce, the last used withdrawal root nonce for the
         * supplier and partition. For fallback withdrawals: max cumulative withdrawal amount, the
         * maximum amount of tokens that can be withdrawn for the supplier's account, including both
         * withdrawals and fallback withdrawals
         * @return proof, the Merkle proof to be used for the authorization
         */
        function _decodeWithdrawalOperatorData(bytes memory _operatorData)
            internal
            pure
            returns (
                address,
                uint256,
                bytes32[] memory
            )
        {
            (, address supplier, uint256 nonce, bytes32[] memory proof) = abi.decode(
                _operatorData,
                (bytes32, address, uint256, bytes32[])
            );
    
            return (supplier, nonce, proof);
        }
    
        /**
         * @notice Extracts the supply nonce from the operator data
         * @param _operatorData The operator data to be decoded
         * @return nonce, the nonce of the supply to be refunded
         */
        function _decodeRefundOperatorData(bytes memory _operatorData) internal pure returns (uint256) {
            (, uint256 nonce) = abi.decode(_operatorData, (bytes32, uint256));
    
            return nonce;
        }
    
        /**********************************************************************************************
         * Merkle Tree Verification
         *********************************************************************************************/
    
        /**
         * @notice Hashes the supplied data and returns the hash to be used in conjunction with a proof
         * to calculate the Merkle tree root
         * @param _supplier The address whose account is authorized
         * @param _partition Source partition of the tokens
         * @param _value Number of tokens to be transferred
         * @param _maxAuthorizedAccountNonce The maximum existing used withdrawal nonce for the
         * supplier and partition
         * @return leaf, the hash of the supplied data
         */
        function _calculateWithdrawalLeaf(
            address _supplier,
            bytes32 _partition,
            uint256 _value,
            uint256 _maxAuthorizedAccountNonce
        ) internal pure returns (bytes32) {
            return
                keccak256(abi.encodePacked(_supplier, _partition, _value, _maxAuthorizedAccountNonce));
        }
    
        /**
         * @notice Hashes the supplied data and returns the hash to be used in conjunction with a proof
         * to calculate the Merkle tree root
         * @param _supplier The address whose account is authorized
         * @param _partition Source partition of the tokens
         * @param _maxCumulativeWithdrawalAmount, the maximum amount of tokens that can be withdrawn
         * for the supplier's account, including both withdrawals and fallback withdrawals
         * @return leaf, the hash of the supplied data
         */
        function _calculateFallbackLeaf(
            address _supplier,
            bytes32 _partition,
            uint256 _maxCumulativeWithdrawalAmount
        ) internal pure returns (bytes32) {
            return keccak256(abi.encodePacked(_supplier, _partition, _maxCumulativeWithdrawalAmount));
        }
    
        /**
         * @notice Calculates the Merkle root for the unique Merkle tree described by the provided
           Merkle proof and leaf hash.
         * @param _merkleProof The sibling node hashes at each level of the tree.
         * @param _leafHash The hash of the leaf data for which merkleProof is an inclusion proof.
         * @return The calculated Merkle root.
         */
        function _calculateMerkleRoot(bytes32[] memory _merkleProof, bytes32 _leafHash)
            private
            pure
            returns (bytes32)
        {
            bytes32 computedHash = _leafHash;
    
            for (uint256 i = 0; i < _merkleProof.length; i++) {
                bytes32 proofElement = _merkleProof[i];
    
                if (computedHash < proofElement) {
                    computedHash = keccak256(abi.encodePacked(computedHash, proofElement));
                } else {
                    computedHash = keccak256(abi.encodePacked(proofElement, computedHash));
                }
            }
    
            return computedHash;
        }
    }