Transaction Hash:
Block:
20075463 at Jun-12-2024 11:31:11 AM +UTC
Transaction Fee:
0.000551928701785054 ETH
$1.35
Gas Used:
25,421 Gas / 21.711525974 Gwei
Emitted Events:
213 |
FlexaCollateralManager.ReleaseRequest( supplier=[Sender] 0x5671fd45912a2f93ea4d1706a4440898bbdb7335, partition=CCCCCCCC01EF57163C6D4D34706D7F8B3445D8DFC790C524E3990EF014E7C578, amount=11459000000000000000000, data=0x )
|
Account State Difference:
Address | Before | After | State Difference | ||
---|---|---|---|---|---|
0x5671fD45...8BBDB7335 |
0.005642975548889651 Eth
Nonce: 284
|
0.005091046847104597 Eth
Nonce: 285
| 0.000551928701785054 | ||
0xdf99A083...17Dc6A555
Miner
| (Flashbots: Builder 2) | 1.583561899184138647 Eth | 1.583564441284138647 Eth | 0.0000025421 |
Execution Trace
FlexaCollateralManager.requestRelease( _partition=CCCCCCCC01EF57163C6D4D34706D7F8B3445D8DFC790C524E3990EF014E7C578, _amount=11459000000000000000000, _data=0x )
requestRelease[FlexaCollateralManager (ln:1463)]
ReleaseRequest[FlexaCollateralManager (ln:1468)]
// 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; } }