ETH Price: $3,872.27 (-0.42%)

Transaction Decoder

Block:
16934151 at Mar-29-2023 04:31:59 PM +UTC
Transaction Fee:
0.002611088099347536 ETH $10.11
Gas Used:
71,312 Gas / 36.614989053 Gwei

Emitted Events:

212 StakingVault.StakeChanged( account=[Sender] 0x73ff800cf158546ba7663ae84fe19d6e09590768, amount=7268218940000000000, lockExpiry=1687883519 )
213 StakingVault.BoostHFTStake( account=[Sender] 0x73ff800cf158546ba7663ae84fe19d6e09590768, amount=7268218940000000000, daysStaked=90 )
214 HFT.Transfer( from=[Sender] 0x73ff800cf158546ba7663ae84fe19d6e09590768, to=[Receiver] StakingVault, amount=7268218940000000000 )

Account State Difference:

  Address   Before After State Difference Code
0x15725391...0A3f068F6
8.421157852596916172 Eth8.421164983796916172 Eth0.0000071312
0x73FF800c...e09590768
0.019697339021506297 Eth
Nonce: 40
0.017086250922158761 Eth
Nonce: 41
0.002611088099347536
0xb3999F65...C942BcADC

Execution Trace

StakingVault.boostHFTStake( amount=7268218940000000000, daysToStake=90 )
  • HFT.transferFrom( src=0x73FF800cf158546BA7663ae84FE19D6e09590768, dst=0x15725391A37A5fFeB04F79cf25DA8460A3f068F6, rawAmount=7268218940000000000 ) => ( True )
    File 1 of 2: StakingVault
    abstract contract Context {
        function _msgSender() internal view virtual returns (address) {
            return msg.sender;
        }
    
        function _msgData() internal view virtual returns (bytes calldata) {
            return msg.data;
        }
    }
    
    abstract contract Ownable is Context {
        address private _owner;
    
        event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
    
        /**
         * @dev Initializes the contract setting the deployer as the initial owner.
         */
        constructor() {
            _transferOwnership(_msgSender());
        }
    
        /**
         * @dev Throws if called by any account other than the owner.
         */
        modifier onlyOwner() {
            _checkOwner();
            _;
        }
    
        /**
         * @dev Returns the address of the current owner.
         */
        function owner() public view virtual returns (address) {
            return _owner;
        }
    
        /**
         * @dev Throws if the sender is not the owner.
         */
        function _checkOwner() internal view virtual {
            require(owner() == _msgSender(), "Ownable: caller is not the owner");
        }
    
        /**
         * @dev Leaves the contract without owner. It will not be possible to call
         * `onlyOwner` functions anymore. Can only be called by the current owner.
         *
         * NOTE: Renouncing ownership will leave the contract without an owner,
         * thereby removing any functionality that is only available to the owner.
         */
        function renounceOwnership() public virtual onlyOwner {
            _transferOwnership(address(0));
        }
    
        /**
         * @dev Transfers ownership of the contract to a new account (`newOwner`).
         * Can only be called by the current owner.
         */
        function transferOwnership(address newOwner) public virtual onlyOwner {
            require(newOwner != address(0), "Ownable: new owner is the zero address");
            _transferOwnership(newOwner);
        }
    
        /**
         * @dev Transfers ownership of the contract to a new account (`newOwner`).
         * Internal function without access restriction.
         */
        function _transferOwnership(address newOwner) internal virtual {
            address oldOwner = _owner;
            _owner = newOwner;
            emit OwnershipTransferred(oldOwner, newOwner);
        }
    }
    
    interface IERC20 {
        /**
         * @dev Emitted when `value` tokens are moved from one account (`from`) to
         * another (`to`).
         *
         * Note that `value` may be zero.
         */
        event Transfer(address indexed from, address indexed to, uint256 value);
    
        /**
         * @dev Emitted when the allowance of a `spender` for an `owner` is set by
         * a call to {approve}. `value` is the new allowance.
         */
        event Approval(address indexed owner, address indexed spender, uint256 value);
    
        /**
         * @dev Returns the amount of tokens in existence.
         */
        function totalSupply() external view returns (uint256);
    
        /**
         * @dev Returns the amount of tokens owned by `account`.
         */
        function balanceOf(address account) external view returns (uint256);
    
        /**
         * @dev Moves `amount` tokens from the caller's account to `to`.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * Emits a {Transfer} event.
         */
        function transfer(address to, uint256 amount) external returns (bool);
    
        /**
         * @dev Returns the remaining number of tokens that `spender` will be
         * allowed to spend on behalf of `owner` through {transferFrom}. This is
         * zero by default.
         *
         * This value changes when {approve} or {transferFrom} are called.
         */
        function allowance(address owner, address spender) external view returns (uint256);
    
        /**
         * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * IMPORTANT: Beware that changing an allowance with this method brings the risk
         * that someone may use both the old and the new allowance by unfortunate
         * transaction ordering. One possible solution to mitigate this race
         * condition is to first reduce the spender's allowance to 0 and set the
         * desired value afterwards:
         * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
         *
         * Emits an {Approval} event.
         */
        function approve(address spender, uint256 amount) external returns (bool);
    
        /**
         * @dev Moves `amount` tokens from `from` to `to` using the
         * allowance mechanism. `amount` is then deducted from the caller's
         * allowance.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * Emits a {Transfer} event.
         */
        function transferFrom(
            address from,
            address to,
            uint256 amount
        ) external returns (bool);
    }
    
    interface IERC20Permit {
        /**
         * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
         * given ``owner``'s signed approval.
         *
         * IMPORTANT: The same issues {IERC20-approve} has related to transaction
         * ordering also apply here.
         *
         * Emits an {Approval} event.
         *
         * Requirements:
         *
         * - `spender` cannot be the zero address.
         * - `deadline` must be a timestamp in the future.
         * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
         * over the EIP712-formatted function arguments.
         * - the signature must use ``owner``'s current nonce (see {nonces}).
         *
         * For more information on the signature format, see the
         * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
         * section].
         */
        function permit(
            address owner,
            address spender,
            uint256 value,
            uint256 deadline,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) external;
    
        /**
         * @dev Returns the current nonce for `owner`. This value must be
         * included whenever a signature is generated for {permit}.
         *
         * Every successful call to {permit} increases ``owner``'s nonce by one. This
         * prevents a signature from being used multiple times.
         */
        function nonces(address owner) external view returns (uint256);
    
        /**
         * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
         */
        // solhint-disable-next-line func-name-mixedcase
        function DOMAIN_SEPARATOR() external view returns (bytes32);
    }
    
    library Address {
        /**
         * @dev Returns true if `account` is a contract.
         *
         * [IMPORTANT]
         * ====
         * It is unsafe to assume that an address for which this function returns
         * false is an externally-owned account (EOA) and not a contract.
         *
         * Among others, `isContract` will return false for the following
         * types of addresses:
         *
         *  - an externally-owned account
         *  - a contract in construction
         *  - an address where a contract will be created
         *  - an address where a contract lived, but was destroyed
         * ====
         *
         * [IMPORTANT]
         * ====
         * You shouldn't rely on `isContract` to protect against flash loan attacks!
         *
         * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
         * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
         * constructor.
         * ====
         */
        function isContract(address account) internal view returns (bool) {
            // This method relies on extcodesize/address.code.length, which returns 0
            // for contracts in construction, since the code is only stored at the end
            // of the constructor execution.
    
            return account.code.length > 0;
        }
    
        /**
         * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
         * `recipient`, forwarding all available gas and reverting on errors.
         *
         * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
         * of certain opcodes, possibly making contracts go over the 2300 gas limit
         * imposed by `transfer`, making them unable to receive funds via
         * `transfer`. {sendValue} removes this limitation.
         *
         * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
         *
         * IMPORTANT: because control is transferred to `recipient`, care must be
         * taken to not create reentrancy vulnerabilities. Consider using
         * {ReentrancyGuard} or the
         * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
         */
        function sendValue(address payable recipient, uint256 amount) internal {
            require(address(this).balance >= amount, "Address: insufficient balance");
    
            (bool success, ) = recipient.call{value: amount}("");
            require(success, "Address: unable to send value, recipient may have reverted");
        }
    
        /**
         * @dev Performs a Solidity function call using a low level `call`. A
         * plain `call` is an unsafe replacement for a function call: use this
         * function instead.
         *
         * If `target` reverts with a revert reason, it is bubbled up by this
         * function (like regular Solidity function calls).
         *
         * Returns the raw returned data. To convert to the expected return value,
         * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
         *
         * Requirements:
         *
         * - `target` must be a contract.
         * - calling `target` with `data` must not revert.
         *
         * _Available since v3.1._
         */
        function functionCall(address target, bytes memory data) internal returns (bytes memory) {
            return functionCallWithValue(target, data, 0, "Address: low-level call failed");
        }
    
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
         * `errorMessage` as a fallback revert reason when `target` reverts.
         *
         * _Available since v3.1._
         */
        function functionCall(
            address target,
            bytes memory data,
            string memory errorMessage
        ) internal returns (bytes memory) {
            return functionCallWithValue(target, data, 0, errorMessage);
        }
    
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but also transferring `value` wei to `target`.
         *
         * Requirements:
         *
         * - the calling contract must have an ETH balance of at least `value`.
         * - the called Solidity function must be `payable`.
         *
         * _Available since v3.1._
         */
        function functionCallWithValue(
            address target,
            bytes memory data,
            uint256 value
        ) internal returns (bytes memory) {
            return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
        }
    
        /**
         * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
         * with `errorMessage` as a fallback revert reason when `target` reverts.
         *
         * _Available since v3.1._
         */
        function functionCallWithValue(
            address target,
            bytes memory data,
            uint256 value,
            string memory errorMessage
        ) internal returns (bytes memory) {
            require(address(this).balance >= value, "Address: insufficient balance for call");
            (bool success, bytes memory returndata) = target.call{value: value}(data);
            return verifyCallResultFromTarget(target, success, returndata, errorMessage);
        }
    
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but performing a static call.
         *
         * _Available since v3.3._
         */
        function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
            return functionStaticCall(target, data, "Address: low-level static call failed");
        }
    
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
         * but performing a static call.
         *
         * _Available since v3.3._
         */
        function functionStaticCall(
            address target,
            bytes memory data,
            string memory errorMessage
        ) internal view returns (bytes memory) {
            (bool success, bytes memory returndata) = target.staticcall(data);
            return verifyCallResultFromTarget(target, success, returndata, errorMessage);
        }
    
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but performing a delegate call.
         *
         * _Available since v3.4._
         */
        function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
            return functionDelegateCall(target, data, "Address: low-level delegate call failed");
        }
    
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
         * but performing a delegate call.
         *
         * _Available since v3.4._
         */
        function functionDelegateCall(
            address target,
            bytes memory data,
            string memory errorMessage
        ) internal returns (bytes memory) {
            (bool success, bytes memory returndata) = target.delegatecall(data);
            return verifyCallResultFromTarget(target, success, returndata, errorMessage);
        }
    
        /**
         * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
         * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
         *
         * _Available since v4.8._
         */
        function verifyCallResultFromTarget(
            address target,
            bool success,
            bytes memory returndata,
            string memory errorMessage
        ) internal view returns (bytes memory) {
            if (success) {
                if (returndata.length == 0) {
                    // only check isContract if the call was successful and the return data is empty
                    // otherwise we already know that it was a contract
                    require(isContract(target), "Address: call to non-contract");
                }
                return returndata;
            } else {
                _revert(returndata, errorMessage);
            }
        }
    
        /**
         * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
         * revert reason or using the provided one.
         *
         * _Available since v4.3._
         */
        function verifyCallResult(
            bool success,
            bytes memory returndata,
            string memory errorMessage
        ) internal pure returns (bytes memory) {
            if (success) {
                return returndata;
            } else {
                _revert(returndata, errorMessage);
            }
        }
    
        function _revert(bytes memory returndata, string memory errorMessage) private pure {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly
                /// @solidity memory-safe-assembly
                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
    
    library SafeERC20 {
        using Address for address;
    
        function safeTransfer(
            IERC20 token,
            address to,
            uint256 value
        ) internal {
            _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
        }
    
        function safeTransferFrom(
            IERC20 token,
            address from,
            address to,
            uint256 value
        ) internal {
            _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
        }
    
        /**
         * @dev Deprecated. This function has issues similar to the ones found in
         * {IERC20-approve}, and its usage is discouraged.
         *
         * Whenever possible, use {safeIncreaseAllowance} and
         * {safeDecreaseAllowance} instead.
         */
        function safeApprove(
            IERC20 token,
            address spender,
            uint256 value
        ) internal {
            // safeApprove should only be called when setting an initial allowance,
            // or when resetting it to zero. To increase and decrease it, use
            // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
            require(
                (value == 0) || (token.allowance(address(this), spender) == 0),
                "SafeERC20: approve from non-zero to non-zero allowance"
            );
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
        }
    
        function safeIncreaseAllowance(
            IERC20 token,
            address spender,
            uint256 value
        ) internal {
            uint256 newAllowance = token.allowance(address(this), spender) + value;
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
        }
    
        function safeDecreaseAllowance(
            IERC20 token,
            address spender,
            uint256 value
        ) internal {
            unchecked {
                uint256 oldAllowance = token.allowance(address(this), spender);
                require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
                uint256 newAllowance = oldAllowance - value;
                _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
            }
        }
    
        function safePermit(
            IERC20Permit token,
            address owner,
            address spender,
            uint256 value,
            uint256 deadline,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) internal {
            uint256 nonceBefore = token.nonces(owner);
            token.permit(owner, spender, value, deadline, v, r, s);
            uint256 nonceAfter = token.nonces(owner);
            require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
        }
    
        /**
         * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
         * on the return value: the return value is optional (but if data is returned, it must not be false).
         * @param token The token targeted by the call.
         * @param data The call data (encoded using abi.encode or one of its variants).
         */
        function _callOptionalReturn(IERC20 token, bytes memory data) private {
            // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
            // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
            // the target address contains contract code and also asserts for success in the low-level call.
    
            bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
            if (returndata.length > 0) {
                // Return data is optional
                require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
            }
        }
    }
    
    abstract contract ReentrancyGuard {
        // Booleans are more expensive than uint256 or any type that takes up a full
        // word because each write operation emits an extra SLOAD to first read the
        // slot's contents, replace the bits taken up by the boolean, and then write
        // back. This is the compiler's defense against contract upgrades and
        // pointer aliasing, and it cannot be disabled.
    
        // The values being non-zero value makes deployment a bit more expensive,
        // but in exchange the refund on every call to nonReentrant will be lower in
        // amount. Since refunds are capped to a percentage of the total
        // transaction's gas, it is best to keep them low in cases like this one, to
        // increase the likelihood of the full refund coming into effect.
        uint256 private constant _NOT_ENTERED = 1;
        uint256 private constant _ENTERED = 2;
    
        uint256 private _status;
    
        constructor() {
            _status = _NOT_ENTERED;
        }
    
        /**
         * @dev Prevents a contract from calling itself, directly or indirectly.
         * Calling a `nonReentrant` function from another `nonReentrant`
         * function is not supported. It is possible to prevent this from happening
         * by making the `nonReentrant` function external, and making it call a
         * `private` function that does the actual work.
         */
        modifier nonReentrant() {
            _nonReentrantBefore();
            _;
            _nonReentrantAfter();
        }
    
        function _nonReentrantBefore() private {
            // On the first call to nonReentrant, _status will be _NOT_ENTERED
            require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
    
            // Any calls to nonReentrant after this point will fail
            _status = _ENTERED;
        }
    
        function _nonReentrantAfter() private {
            // By storing the original value once again, a refund is triggered (see
            // https://eips.ethereum.org/EIPS/eip-2200)
            _status = _NOT_ENTERED;
        }
    }
    
    interface IStakingVault {
        /// @notice Represents the stake a user has in the vault.
        struct Stake {
            uint128 amount;
            uint64 lockExpiry;
        }
    
        // Events.
    
        /// @notice Emitted every time a user's stake changes.
        event StakeChanged(
            address indexed account,
            uint128 amount,
            uint64 lockExpiry
        );
    
        /// @notice Emitted when a user boosts either the value or the lock of their stake.
        event BoostHFTStake(
            address indexed account,
            uint128 amount,
            uint64 daysStaked
        );
    
        /// @notice Emitted when HFT is withdrawn.
        event WithdrawHFT(
            address indexed account,
            uint128 amountWithdrawn,
            uint128 amountRestaked
        );
    
        /// @notice Emitted when a stake is transferred to a different vault.
        event TransferHFTStake(
            address indexed account,
            address targetVault,
            uint128 amount
        );
    
        /// @notice Emitted when the max number of staking days is updated.
        event UpdateMaxDaysToStake(uint16 maxDaysToStake);
    
        /// @notice Emitted when a source vault authorization status changes.
        event UpdateSourceVaultAuthorization(address vault, bool isAuthorized);
    
        /// @notice Emitted when a target vault authorization status changes.
        event UpdateTargetVaultAuthorization(address vault, bool isAuthorized);
    
        // Auto-generated functions.
    
        /// @notice Returns the stake that a user has.
        function stakes(address user) external returns (uint128, uint64);
    
        /// @notice Returns the authorization status of a vault to receive HFT from.
        /// @param vault The source vault.
        /// @return The authorization status.
        function sourceVaultAuthorization(address vault) external returns (bool);
    
        /// @notice Returns the authorization status of a vault to send HFT to.
        /// @param vault The source vault.
        /// @return The authorization status.
        function targetVaultAuthorization(address vault) external returns (bool);
    
        // Functions.
    
        /// @notice The total (voting) power of a user's stake.
        /// @param user The user to compute the power for.
        /// @return Total stake power.
        function getStakePower(address user) external view returns (uint256);
    
        /// @notice Increases the amount or the lock of a stake, or both.
        /// @param amount Amount to increase the stake by.
        /// @param daysToStake Days to increase the stake lock by.
        function boostHFTStake(uint128 amount, uint16 daysToStake) external;
    
        /**
         * @notice Increases the amount or the lock of a stake, or both.
         *
         * Uses an ERC-721 permit for HFT allowance.
         */
        /// @param amount Amount to increase the stake by.
        /// @param daysToStake Days to increase the stake lock by.
        /// @param deadline Deadline of permit.
        /// @param v v-part of the permit signature.
        /// @param r r-part of the permit signature.
        /// @param s s-part of the permit signature.
        /// @param approvalAmount Amount of HFT to spend that the permit approves.
        function boostHFTStakeWithPermit(
            uint128 amount,
            uint16 daysToStake,
            uint256 deadline,
            uint8 v,
            bytes32 r,
            bytes32 s,
            uint256 approvalAmount
        ) external;
    
        /// @notice Increases the HFT amount of a user's stake.
        /// @param user The user to increase the stake for.
        /// @param amount Amount by which the stake needs to be increased.
        /// @dev Can only be called by a contract.
        function increaseHFTStakeAmountFor(address user, uint128 amount) external;
    
        /// @notice Withdraws HFT to the user.
        /// @param amountToRestake Amount of HFT to re-stake instead of withdrawing.
        /// @param daysToRestake Number of days to lock the re-staked portion.
        function withdrawHFT(
            uint128 amountToRestake,
            uint16 daysToRestake
        ) external;
    
        /// @notice Transfers a user's stake to another vault.
        /// @param targetVault The address of the target vault.
        function transferHFTStake(address targetVault) external;
    
        /// @notice Receives a stake transfer that is issued via transferHFTStake.
        /// @param user The user to receive the transfer for.
        /// @param amount Amount of stake to receive.
        /// @param lockExpiry Lock expiry in the source vault.
        function receiveHFTStakeTransfer(
            address user,
            uint128 amount,
            uint64 lockExpiry
        ) external;
    
        // Admin.
    
        /// @notice Updates the max staking period, in days.
        /// @param maxDaysToStake The new max number of days a user is allowed to stake.
        function updateMaxDaysToStake(uint16 maxDaysToStake) external;
    
        /// @notice Updates the authorization status of a source vault, for stake transfer.
        /// @param vault The vault to update the authorization for.
        /// @param isAuthorized The new authorization status.
        function updateSourceVaultAuthorization(
            address vault,
            bool isAuthorized
        ) external;
    
        /// @notice Updates the authorization status of a target vault, for stake transfer.
        /// @param vault The vault to update the authorization for.
        /// @param isAuthorized The new authorization status.
        function updateTargetVaultAuthorization(
            address vault,
            bool isAuthorized
        ) external;
    }
    
    contract StakingVault is IStakingVault, ReentrancyGuard, Ownable {
        using SafeERC20 for IERC20;
        using SafeERC20 for IERC20Permit;
        using Address for address;
    
        address public immutable hft;
    
        uint16 public maxDaysToStake;
    
        mapping(address => Stake) public stakes;
    
        mapping(address => bool) public sourceVaultAuthorization;
        mapping(address => bool) public targetVaultAuthorization;
    
        constructor(address _hft) {
            require(
                _hft != address(0),
                'StakingVault::constructor HFT is 0 address.'
            );
            hft = _hft;
    
            // 4 years by default.
            maxDaysToStake = 4 * 365;
        }
    
        function getStakePower(
            address user
        ) external view override returns (uint256) {
            uint256 timeUntilExpiry = 0;
    
            Stake memory stake = stakes[user];
            if (stake.lockExpiry > block.timestamp) {
                timeUntilExpiry = uint256(stake.lockExpiry) - block.timestamp;
            }
    
            /**
             * @dev We give 1 power for every 4 years of HFT collectively locked
             * in the vault by the user.
             */
            return (stake.amount * timeUntilExpiry) / (4 * (365 days));
        }
    
        function boostHFTStake(
            uint128 amount,
            uint16 daysToStake
        ) external override {
            _boostHFTStake(msg.sender, amount, daysToStake);
    
            emit BoostHFTStake(msg.sender, amount, daysToStake);
    
            IERC20(hft).safeTransferFrom(msg.sender, address(this), amount);
        }
    
        function boostHFTStakeWithPermit(
            uint128 amount,
            uint16 daysToStake,
            uint256 deadline,
            uint8 v,
            bytes32 r,
            bytes32 s,
            uint256 approvalAmount
        ) external override {
            _boostHFTStake(msg.sender, amount, daysToStake);
    
            emit BoostHFTStake(msg.sender, amount, daysToStake);
    
            IERC20Permit(hft).safePermit(
                msg.sender,
                address(this),
                approvalAmount,
                deadline,
                v,
                r,
                s
            );
    
            IERC20(hft).safeTransferFrom(msg.sender, address(this), amount);
        }
    
        function increaseHFTStakeAmountFor(
            address user,
            uint128 amount
        ) external override {
            require(
                msg.sender.isContract(),
                'StakingVault::increaseHFTStakeAmountFor Caller should be contract.'
            );
    
            _boostHFTStake(user, amount, 0);
    
            emit BoostHFTStake(user, amount, 0);
    
            IERC20(hft).safeTransferFrom(msg.sender, address(this), amount);
        }
    
        function withdrawHFT(
            uint128 amountToRestake,
            uint16 daysToRestake
        ) external override {
            Stake memory currentStake = stakes[msg.sender];
    
            require(
                currentStake.lockExpiry <= block.timestamp,
                'StakingVault::withdrawHFT HFT is locked.'
            );
            require(
                currentStake.amount > 0,
                'StakingVault::withdrawHFT No HFT staked.'
            );
    
            uint128 amountToWithdraw = currentStake.amount;
    
            if (amountToRestake > 0) {
                require(
                    daysToRestake > 0,
                    'StakingVault::withdrawHFT Time lock not specified.'
                );
                require(
                    amountToRestake <= currentStake.amount,
                    'StakingVault::withdrawHFT Re-stake amount too high.'
                );
    
                amountToWithdraw -= amountToRestake;
            }
    
            currentStake.amount = 0;
    
            stakes[msg.sender] = currentStake;
    
            emit StakeChanged(
                msg.sender,
                currentStake.amount,
                currentStake.lockExpiry
            );
    
            if (amountToRestake > 0) {
                _boostHFTStake(msg.sender, amountToRestake, daysToRestake);
            }
    
            emit WithdrawHFT(msg.sender, amountToWithdraw, amountToRestake);
    
            if (amountToWithdraw > 0) {
                IERC20(hft).safeTransfer(msg.sender, amountToWithdraw);
            }
        }
    
        function transferHFTStake(
            address targetVault
        ) external override nonReentrant {
            require(
                targetVaultAuthorization[targetVault],
                'StakingVault::transferHFTStake Target Vault not authorized.'
            );
    
            Stake memory currentStake = stakes[msg.sender];
    
            require(
                currentStake.amount > 0,
                'StakingVault::transferHFTStake No HFT locked.'
            );
    
            uint128 amountToTransfer = currentStake.amount;
            uint64 lockExpiryToTransfer = currentStake.lockExpiry;
    
            currentStake.amount = 0;
            currentStake.lockExpiry = 0;
    
            stakes[msg.sender] = currentStake;
    
            emit StakeChanged(
                msg.sender,
                currentStake.amount,
                currentStake.lockExpiry
            );
    
            emit TransferHFTStake(msg.sender, targetVault, amountToTransfer);
    
            IERC20(hft).safeIncreaseAllowance(
                targetVault,
                uint256(amountToTransfer)
            );
    
            IStakingVault(targetVault).receiveHFTStakeTransfer(
                msg.sender,
                amountToTransfer,
                lockExpiryToTransfer
            );
    
            require(
                IERC20(hft).allowance(address(this), targetVault) == 0,
                'StakingVault::transferHFTStake HFT not spent.'
            );
        }
    
        function receiveHFTStakeTransfer(
            address user,
            uint128 amount,
            uint64 lockExpiry
        ) external override nonReentrant {
            require(
                sourceVaultAuthorization[msg.sender],
                'StakingVault::receiveHFTStakeTransfer Source Vault not authorized.'
            );
            uint64 newExpiry = lockExpiry;
    
            Stake memory currentStake = stakes[user];
    
            if (currentStake.lockExpiry > newExpiry) {
                newExpiry = currentStake.lockExpiry;
            }
    
            require(
                newExpiry <=
                    (uint64(block.timestamp) +
                        uint64(maxDaysToStake) *
                        uint64(1 days)),
                'StakingVault::receiveHFTStakeTransfer Time lock too high.'
            );
    
            currentStake.lockExpiry = newExpiry;
    
            require(
                type(uint128).max - amount > currentStake.amount,
                'StakingVault::receiveHFTStakeTransfer amount too high.'
            );
    
            currentStake.amount += amount;
    
            stakes[user] = currentStake;
    
            IERC20(hft).safeTransferFrom(msg.sender, address(this), amount);
        }
    
        // Admin
    
        function updateMaxDaysToStake(
            uint16 newMaxDaysToStake
        ) external override onlyOwner {
            require(
                newMaxDaysToStake != maxDaysToStake,
                'StakingVault::updateMaxDaysToStake Number has not changed.'
            );
            maxDaysToStake = newMaxDaysToStake;
    
            emit UpdateMaxDaysToStake(maxDaysToStake);
        }
    
        function updateSourceVaultAuthorization(
            address vault,
            bool isAuthorized
        ) external override onlyOwner {
            require(
                vault != address(this),
                'StakingVault::updateSourceVaultAuthorization Cannot self-authorize.'
            );
            require(
                sourceVaultAuthorization[vault] != isAuthorized,
                'StakingVault::updateSourceVaultAuthorization No-op.'
            );
            sourceVaultAuthorization[vault] = isAuthorized;
    
            emit UpdateSourceVaultAuthorization(vault, isAuthorized);
        }
    
        function updateTargetVaultAuthorization(
            address vault,
            bool isAuthorized
        ) external override onlyOwner {
            require(
                vault != address(this),
                'StakingVault::updateTargetVaultAuthorization Cannot self-authorize.'
            );
            require(
                targetVaultAuthorization[vault] != isAuthorized,
                'StakingVault::updateTargetVaultAuthorization No-op.'
            );
            targetVaultAuthorization[vault] = isAuthorized;
    
            emit UpdateTargetVaultAuthorization(vault, isAuthorized);
        }
    
        function renounceOwnership() public view override onlyOwner {
            revert('StakingVault::renounceOwnership Cannot renounce ownership.');
        }
    
        // Internal functions.
    
        function _boostHFTStake(
            address user,
            uint128 amount,
            uint16 daysToStake
        ) internal {
            require(
                amount > 0 || daysToStake > 0,
                'StakingVault::_boostHFTStake Amount or days have to be > 0'
            );
            Stake memory currentStake = stakes[user];
    
            if (daysToStake > 0) {
                uint64 timeUntilExpiry = 0;
                if (currentStake.lockExpiry > block.timestamp) {
                    timeUntilExpiry =
                        currentStake.lockExpiry -
                        uint64(block.timestamp);
                }
    
                uint64 extraLockTime = uint64(daysToStake) * uint64(1 days);
    
                require(
                    extraLockTime + timeUntilExpiry <=
                        uint64(maxDaysToStake) * uint64(1 days),
                    'StakingVault::_boostHFTStake Time lock too high'
                );
    
                if (timeUntilExpiry > 0) {
                    currentStake.lockExpiry += extraLockTime;
                } else {
                    currentStake.lockExpiry =
                        uint64(block.timestamp) +
                        extraLockTime;
                }
            }
    
            if (amount > 0) {
                require(
                    type(uint128).max - currentStake.amount > amount,
                    'StakingVault::_boostHFTStake amount too high.'
                );
                currentStake.amount += amount;
            }
    
            stakes[user] = currentStake;
    
            emit StakeChanged(user, currentStake.amount, currentStake.lockExpiry);
        }
    }

    File 2 of 2: HFT
    /**
     * SPDX-License-Identifier: UNLICENSED
     */
    pragma solidity 0.8.11;
    
    library SafeMath {
        /**
         * @dev Returns the addition of two unsigned integers, with an overflow flag.
         *
         * _Available since v3.4._
         */
        function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
            unchecked {
                uint256 c = a + b;
                if (c < a) return (false, 0);
                return (true, c);
            }
        }
    
        /**
         * @dev Returns the substraction of two unsigned integers, with an overflow flag.
         *
         * _Available since v3.4._
         */
        function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
            unchecked {
                if (b > a) return (false, 0);
                return (true, a - b);
            }
        }
    
        /**
         * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
         *
         * _Available since v3.4._
         */
        function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
            unchecked {
                // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
                // benefit is lost if 'b' is also tested.
                // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
                if (a == 0) return (true, 0);
                uint256 c = a * b;
                if (c / a != b) return (false, 0);
                return (true, c);
            }
        }
    
        /**
         * @dev Returns the division of two unsigned integers, with a division by zero flag.
         *
         * _Available since v3.4._
         */
        function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
            unchecked {
                if (b == 0) return (false, 0);
                return (true, a / b);
            }
        }
    
        /**
         * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
         *
         * _Available since v3.4._
         */
        function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
            unchecked {
                if (b == 0) return (false, 0);
                return (true, a % b);
            }
        }
    
        /**
         * @dev Returns the addition of two unsigned integers, reverting on
         * overflow.
         *
         * Counterpart to Solidity's `+` operator.
         *
         * Requirements:
         *
         * - Addition cannot overflow.
         */
        function add(uint256 a, uint256 b) internal pure returns (uint256) {
            return a + b;
        }
    
        /**
         * @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 a - b;
        }
    
        /**
         * @dev Returns the multiplication of two unsigned integers, reverting on
         * overflow.
         *
         * Counterpart to Solidity's `*` operator.
         *
         * Requirements:
         *
         * - Multiplication cannot overflow.
         */
        function mul(uint256 a, uint256 b) internal pure returns (uint256) {
            return a * b;
        }
    
        /**
         * @dev Returns the integer division of two unsigned integers, reverting on
         * division by zero. The result is rounded towards zero.
         *
         * Counterpart to Solidity's `/` operator.
         *
         * Requirements:
         *
         * - The divisor cannot be zero.
         */
        function div(uint256 a, uint256 b) internal pure returns (uint256) {
            return a / b;
        }
    
        /**
         * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
         * reverting when dividing by zero.
         *
         * Counterpart to Solidity's `%` operator. This function uses a `revert`
         * opcode (which leaves remaining gas untouched) while Solidity uses an
         * invalid opcode to revert (consuming all remaining gas).
         *
         * Requirements:
         *
         * - The divisor cannot be zero.
         */
        function mod(uint256 a, uint256 b) internal pure returns (uint256) {
            return a % b;
        }
    
        /**
         * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
         * overflow (when the result is negative).
         *
         * CAUTION: This function is deprecated because it requires allocating memory for the error
         * message unnecessarily. For custom revert reasons use {trySub}.
         *
         * Counterpart to Solidity's `-` operator.
         *
         * Requirements:
         *
         * - Subtraction cannot overflow.
         */
        function sub(
            uint256 a,
            uint256 b,
            string memory errorMessage
        ) internal pure returns (uint256) {
            unchecked {
                require(b <= a, errorMessage);
                return a - b;
            }
        }
    
        /**
         * @dev Returns the integer division of two unsigned integers, reverting with custom message on
         * division by zero. The result is rounded towards zero.
         *
         * 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) {
            unchecked {
                require(b > 0, errorMessage);
                return a / b;
            }
        }
    
        /**
         * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
         * reverting with custom message when dividing by zero.
         *
         * CAUTION: This function is deprecated because it requires allocating memory for the error
         * message unnecessarily. For custom revert reasons use {tryMod}.
         *
         * Counterpart to Solidity's `%` operator. This function uses a `revert`
         * opcode (which leaves remaining gas untouched) while Solidity uses an
         * invalid opcode to revert (consuming all remaining gas).
         *
         * Requirements:
         *
         * - The divisor cannot be zero.
         */
        function mod(
            uint256 a,
            uint256 b,
            string memory errorMessage
        ) internal pure returns (uint256) {
            unchecked {
                require(b > 0, errorMessage);
                return a % b;
            }
        }
    }
    
    contract HFT {
        /// @notice EIP-20 token name for this token
        string public constant name = 'Hashflow';
    
        /// @notice EIP-20 token symbol for this token
        string public constant symbol = 'HFT';
    
        /// @notice EIP-20 token decimals for this token
        uint8 public constant decimals = 18;
    
        /// @notice Total number of tokens in circulation
        uint256 public totalSupply = 1_000_000_000e18; // 1 billion HFT
    
        /// @notice Address which may mint new tokens
        address public minter;
    
        /// @notice The timestamp after which minting may occur (must be set to 4 years)
        uint256 public mintingAllowedAfter;
    
        /// @notice Minimum time between mints
        uint32 public constant minimumTimeBetweenMints = 1 days * 365;
    
        /// @notice Cap on the percentage of totalSupply that can be minted at each mint (set to 5% inflation currently)
        uint8 public mintCap = 5;
    
        /// @notice Allowance amounts on behalf of others
        mapping(address => mapping(address => uint96)) internal allowances;
    
        /// @notice Official record of token balances for each account
        mapping(address => uint96) internal balances;
    
        /// @notice A record of each accounts delegate
        mapping(address => address) public delegates;
    
        /// @notice A checkpoint for marking number of votes from a given block
        struct Checkpoint {
            uint32 fromBlock;
            uint96 votes;
        }
    
        /// @notice A record of votes checkpoints for each account, by index
        mapping(address => mapping(uint32 => Checkpoint)) public checkpoints;
    
        /// @notice The number of checkpoints for each account
        mapping(address => uint32) public numCheckpoints;
    
        /// @notice The EIP-712 typehash for the contract's domain
        bytes32 public constant DOMAIN_TYPEHASH =
            keccak256(
                'EIP712Domain(string name,uint256 chainId,address verifyingContract)'
            );
    
        /// @notice The EIP-712 typehash for the delegation struct used by the contract
        bytes32 public constant DELEGATION_TYPEHASH =
            keccak256('Delegation(address delegatee,uint256 nonce,uint256 expiry)');
    
        /// @notice The EIP-712 typehash for the permit struct used by the contract
        bytes32 public constant PERMIT_TYPEHASH =
            keccak256(
                'Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)'
            );
    
        /// @notice A record of states for signing / validating signatures
        mapping(address => uint256) public nonces;
    
        /// @notice An event that is emitted when the minter address is changed
        event MinterChanged(address minter, address newMinter);
    
        /// @notice An event that is emitted when the mint percentage is changed
        event MintCapChanged(uint256 newMintCap);
    
        /// @notice An event thats emitted when an account changes its delegate
        event DelegateChanged(
            address indexed delegator,
            address indexed fromDelegate,
            address indexed toDelegate
        );
    
        /// @notice An event thats emitted when a delegate account's vote balance changes
        event DelegateVotesChanged(
            address indexed delegate,
            uint256 previousBalance,
            uint256 newBalance
        );
    
        /// @notice The standard EIP-20 transfer event
        event Transfer(address indexed from, address indexed to, uint256 amount);
    
        /// @notice The standard EIP-20 approval event
        event Approval(
            address indexed owner,
            address indexed spender,
            uint256 amount
        );
    
        /**
         * @notice Construct a new Hashflow token
         * @param account The initial account to grant all the tokens
         * @param minter_ The account with minting ability
         * @param mintingAllowedAfter_ The timestamp after which minting may occur
         */
        constructor(
            address account,
            address minter_,
            uint256 mintingAllowedAfter_
        ) {
            require(
                mintingAllowedAfter_ >= block.timestamp + 1460 days,
                'HFT::constructor: minting can only begin after 4 years'
            );
    
            require(
                minter_ != address(0),
                'HFT::constructor: minter_ cannot be zero address'
            );
    
            require(
                account != address(0),
                'HFT::constructor: account cannot be zero address'
            );
    
            balances[account] = uint96(totalSupply);
            emit Transfer(address(0), account, totalSupply);
            minter = minter_;
            emit MinterChanged(address(0), minter);
            mintingAllowedAfter = mintingAllowedAfter_;
        }
    
        /**
         * @notice Change the minter address
         * @param minter_ The address of the new minter
         */
        function setMinter(address minter_) external {
            require(
                minter_ != address(0),
                'HFT::setMinter: minter_ cannot be zero address'
            );
            require(
                msg.sender == minter,
                'HFT::setMinter: only the minter can change the minter address'
            );
            minter = minter_;
            emit MinterChanged(minter, minter_);
        }
    
        function setMintCap(uint256 mintCap_) external {
            require(
                msg.sender == minter,
                'HFT::setMintCap: only the minter can change the mint cap'
            );
            require(
                mintCap_ <= 100,
                'HFT::setMintCap: mint cap should be between 0 and 100'
            );
            mintCap = uint8(mintCap_);
            emit MintCapChanged(uint256(mintCap));
        }
    
        /**
         * @notice Mint new tokens
         * @param dst The address of the destination account
    
         */
        function mint(address dst) external {
            require(msg.sender == minter, 'HFT::mint: only the minter can mint');
            require(
                block.timestamp >= mintingAllowedAfter,
                'HFT::mint: minting not allowed yet or exceeds mint cap'
            );
            require(
                dst != address(0),
                'HFT::mint: cannot transfer to the zero address'
            );
    
            // record the mint
            mintingAllowedAfter = SafeMath.add(
                block.timestamp,
                minimumTimeBetweenMints
            );
    
            uint96 amount = safe96(
                SafeMath.div(SafeMath.mul(totalSupply, uint256(mintCap)), 100),
                'HFT::mint: amount exceeds 96 bits'
            );
    
            totalSupply = safe96(
                SafeMath.add(totalSupply, amount),
                'HFT::mint: totalSupply exceeds 96 bits'
            );
    
            // transfer the amount to the recipient
            balances[dst] = add96(
                balances[dst],
                amount,
                'HFT::mint: transfer amount overflows'
            );
            emit Transfer(address(0), dst, amount);
    
            // move delegates
            _moveDelegates(address(0), delegates[dst], amount);
        }
    
        /**
         * @notice Get the number of tokens `spender` is approved to spend on behalf of `account`
         * @param account The address of the account holding the funds
         * @param spender The address of the account spending the funds
         * @return The number of tokens approved
         */
        function allowance(address account, address spender)
            external
            view
            returns (uint256)
        {
            return allowances[account][spender];
        }
    
        /**
         * @notice Approve `spender` to transfer up to `amount` from `src`
         * @dev This will overwrite the approval amount for `spender`
         *  and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve)
         * @param spender The address of the account which may transfer tokens
         * @param rawAmount The number of tokens that are approved (2^256-1 means infinite)
         * @return Whether or not the approval succeeded
         */
        function approve(address spender, uint256 rawAmount)
            external
            returns (bool)
        {
            _approve(msg.sender, spender, rawAmount);
            return true;
        }
    
        /**
         * @notice Atomically increases the allowance granted to `spender` by the caller.
         * @dev This is an alternative to {approve} that can be used as a mitigation for
         * problems described in {IERC20-approve}.
         * @param spender The address of the account which may transfer tokens
         * @param rawAmount The number of tokens that are approved (2^256-1 means infinite)
         * @return Whether or not the approval succeeded
         */
        function increaseAllowance(address spender, uint256 rawAmount)
            external
            returns (bool)
        {
            _approve(
                msg.sender,
                spender,
                allowances[msg.sender][spender] + rawAmount
            );
            return true;
        }
    
        /**
         * @notice Atomically increases the allowance granted to `spender` by the caller.
         * @dev This is an alternative to {approve} that can be used as a mitigation for
         * problems described in {IERC20-approve}.
         * @param spender The address of the account which may transfer tokens
         * @param rawAmount The number of tokens that are approved (2^256-1 means infinite)
         * @return Whether or not the approval succeeded
         */
        function decreaseAllowance(address spender, uint256 rawAmount)
            external
            returns (bool)
        {
            _approve(
                msg.sender,
                spender,
                allowances[msg.sender][spender] - rawAmount
            );
            return true;
        }
    
        /**
         * @notice Triggers an approval from owner to spends
         * @param owner The address to approve from
         * @param spender The address to be approved
         * @param rawAmount The number of tokens that are approved (2^256-1 means infinite)
         * @param deadline The time at which to expire the signature
         * @param v The recovery byte of the signature
         * @param r Half of the ECDSA signature pair
         * @param s Half of the ECDSA signature pair
         */
        function permit(
            address owner,
            address spender,
            uint256 rawAmount,
            uint256 deadline,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) external {
            uint96 amount;
            if (rawAmount == type(uint256).max) {
                amount = type(uint96).max;
            } else {
                amount = safe96(rawAmount, 'HFT::permit: amount exceeds 96 bits');
            }
    
            bytes32 domainSeparator = keccak256(
                abi.encode(
                    DOMAIN_TYPEHASH,
                    keccak256(bytes(name)),
                    getChainId(),
                    address(this)
                )
            );
            bytes32 structHash = keccak256(
                abi.encode(
                    PERMIT_TYPEHASH,
                    owner,
                    spender,
                    rawAmount,
                    nonces[owner]++,
                    deadline
                )
            );
            bytes32 digest = keccak256(
                abi.encodePacked('\x19\x01', domainSeparator, structHash)
            );
            address signatory = ecrecover(digest, v, r, s);
            require(signatory != address(0), 'HFT::permit: invalid signature');
            require(signatory == owner, 'HFT::permit: unauthorized');
            require(block.timestamp <= deadline, 'HFT::permit: signature expired');
    
            allowances[owner][spender] = amount;
    
            emit Approval(owner, spender, amount);
        }
    
        /**
         * @notice Get the number of tokens held by the `account`
         * @param account The address of the account to get the balance of
         * @return The number of tokens held
         */
        function balanceOf(address account) external view returns (uint256) {
            return balances[account];
        }
    
        /**
         * @notice Transfer `amount` tokens from `msg.sender` to `dst`
         * @param dst The address of the destination account
         * @param rawAmount The number of tokens to transfer
         * @return Whether or not the transfer succeeded
         */
        function transfer(address dst, uint256 rawAmount) external returns (bool) {
            uint96 amount = safe96(
                rawAmount,
                'HFT::transfer: amount exceeds 96 bits'
            );
            _transferTokens(msg.sender, dst, amount);
            return true;
        }
    
        /**
         * @notice Transfer `amount` tokens from `src` to `dst`
         * @param src The address of the source account
         * @param dst The address of the destination account
         * @param rawAmount The number of tokens to transfer
         * @return Whether or not the transfer succeeded
         */
        function transferFrom(
            address src,
            address dst,
            uint256 rawAmount
        ) external returns (bool) {
            address spender = msg.sender;
            uint96 spenderAllowance = allowances[src][spender];
            uint96 amount = safe96(
                rawAmount,
                'HFT::approve: amount exceeds 96 bits'
            );
    
            if (spender != src && spenderAllowance != type(uint96).max) {
                uint96 newAllowance = sub96(
                    spenderAllowance,
                    amount,
                    'HFT::transferFrom: transfer amount exceeds spender allowance'
                );
                allowances[src][spender] = newAllowance;
    
                emit Approval(src, spender, newAllowance);
            }
    
            _transferTokens(src, dst, amount);
            return true;
        }
    
        /**
         * @notice Delegate votes from `msg.sender` to `delegatee`
         * @param delegatee The address to delegate votes to
         */
        function delegate(address delegatee) public {
            return _delegate(msg.sender, delegatee);
        }
    
        /**
         * @notice Delegates votes from signatory to `delegatee`
         * @param delegatee The address to delegate votes to
         * @param nonce The contract state required to match the signature
         * @param expiry The time at which to expire the signature
         * @param v The recovery byte of the signature
         * @param r Half of the ECDSA signature pair
         * @param s Half of the ECDSA signature pair
         */
        function delegateBySig(
            address delegatee,
            uint256 nonce,
            uint256 expiry,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) public {
            bytes32 domainSeparator = keccak256(
                abi.encode(
                    DOMAIN_TYPEHASH,
                    keccak256(bytes(name)),
                    getChainId(),
                    address(this)
                )
            );
            bytes32 structHash = keccak256(
                abi.encode(DELEGATION_TYPEHASH, delegatee, nonce, expiry)
            );
            bytes32 digest = keccak256(
                abi.encodePacked('\x19\x01', domainSeparator, structHash)
            );
            address signatory = ecrecover(digest, v, r, s);
            require(
                signatory != address(0),
                'HFT::delegateBySig: invalid signature'
            );
            require(
                nonce == nonces[signatory]++,
                'HFT::delegateBySig: invalid nonce'
            );
            require(
                block.timestamp <= expiry,
                'HFT::delegateBySig: signature expired'
            );
            return _delegate(signatory, delegatee);
        }
    
        /**
         * @notice Gets the current votes balance for `account`
         * @param account The address to get votes balance
         * @return The number of current votes for `account`
         */
        function getCurrentVotes(address account) external view returns (uint96) {
            uint32 nCheckpoints = numCheckpoints[account];
            return
                nCheckpoints > 0 ? checkpoints[account][nCheckpoints - 1].votes : 0;
        }
    
        /**
         * @notice Determine the prior number of votes for an account as of a block number
         * @dev Block number must be a finalized block or else this function will revert to prevent misinformation.
         * @param account The address of the account to check
         * @param blockNumber The block number to get the vote balance at
         * @return The number of votes the account had as of the given block
         */
        function getPriorVotes(address account, uint256 blockNumber)
            public
            view
            returns (uint96)
        {
            require(
                blockNumber < block.number,
                'HFT::getPriorVotes: not yet determined'
            );
    
            uint32 nCheckpoints = numCheckpoints[account];
            if (nCheckpoints == 0) {
                return 0;
            }
    
            // First check most recent balance
            if (checkpoints[account][nCheckpoints - 1].fromBlock <= blockNumber) {
                return checkpoints[account][nCheckpoints - 1].votes;
            }
    
            // Next check implicit zero balance
            if (checkpoints[account][0].fromBlock > blockNumber) {
                return 0;
            }
    
            uint32 lower = 0;
            uint32 upper = nCheckpoints - 1;
            while (upper > lower) {
                uint32 center = upper - (upper - lower) / 2; // ceil, avoiding overflow
                Checkpoint memory cp = checkpoints[account][center];
                if (cp.fromBlock == blockNumber) {
                    return cp.votes;
                } else if (cp.fromBlock < blockNumber) {
                    lower = center;
                } else {
                    upper = center - 1;
                }
            }
            return checkpoints[account][lower].votes;
        }
    
        function _delegate(address delegator, address delegatee) internal {
            address currentDelegate = delegates[delegator];
            uint96 delegatorBalance = balances[delegator];
            delegates[delegator] = delegatee;
    
            emit DelegateChanged(delegator, currentDelegate, delegatee);
    
            _moveDelegates(currentDelegate, delegatee, delegatorBalance);
        }
    
        function _approve(
            address caller,
            address spender,
            uint256 rawAmount
        ) internal {
            uint96 amount;
            if (rawAmount == type(uint256).max) {
                amount = type(uint96).max;
            } else {
                amount = safe96(rawAmount, 'HFT::approve: amount exceeds 96 bits');
            }
    
            allowances[caller][spender] = amount;
    
            emit Approval(caller, spender, amount);
        }
    
        function _transferTokens(
            address src,
            address dst,
            uint96 amount
        ) internal {
            require(
                src != address(0),
                'HFT::_transferTokens: cannot transfer from the zero address'
            );
            require(
                dst != address(0),
                'HFT::_transferTokens: cannot transfer to the zero address'
            );
    
            balances[src] = sub96(
                balances[src],
                amount,
                'HFT::_transferTokens: transfer amount exceeds balance'
            );
            balances[dst] = add96(
                balances[dst],
                amount,
                'HFT::_transferTokens: transfer amount overflows'
            );
            emit Transfer(src, dst, amount);
    
            _moveDelegates(delegates[src], delegates[dst], amount);
        }
    
        function _moveDelegates(
            address srcRep,
            address dstRep,
            uint96 amount
        ) internal {
            if (srcRep != dstRep && amount > 0) {
                if (srcRep != address(0)) {
                    uint32 srcRepNum = numCheckpoints[srcRep];
                    uint96 srcRepOld = srcRepNum > 0
                        ? checkpoints[srcRep][srcRepNum - 1].votes
                        : 0;
                    uint96 srcRepNew = sub96(
                        srcRepOld,
                        amount,
                        'HFT::_moveDelegates: vote amount underflows'
                    );
                    _writeCheckpoint(srcRep, srcRepNum, srcRepOld, srcRepNew);
                }
    
                if (dstRep != address(0)) {
                    uint32 dstRepNum = numCheckpoints[dstRep];
                    uint96 dstRepOld = dstRepNum > 0
                        ? checkpoints[dstRep][dstRepNum - 1].votes
                        : 0;
                    uint96 dstRepNew = add96(
                        dstRepOld,
                        amount,
                        'HFT::_moveDelegates: vote amount overflows'
                    );
                    _writeCheckpoint(dstRep, dstRepNum, dstRepOld, dstRepNew);
                }
            }
        }
    
        function _writeCheckpoint(
            address delegatee,
            uint32 nCheckpoints,
            uint96 oldVotes,
            uint96 newVotes
        ) internal {
            uint32 blockNumber = safe32(
                block.number,
                'HFT::_writeCheckpoint: block number exceeds 32 bits'
            );
    
            if (
                nCheckpoints > 0 &&
                checkpoints[delegatee][nCheckpoints - 1].fromBlock == blockNumber
            ) {
                checkpoints[delegatee][nCheckpoints - 1].votes = newVotes;
            } else {
                checkpoints[delegatee][nCheckpoints] = Checkpoint(
                    blockNumber,
                    newVotes
                );
                numCheckpoints[delegatee] = nCheckpoints + 1;
            }
    
            emit DelegateVotesChanged(delegatee, oldVotes, newVotes);
        }
    
        function safe32(uint256 n, string memory errorMessage)
            internal
            pure
            returns (uint32)
        {
            require(n < 2**32, errorMessage);
            return uint32(n);
        }
    
        function safe96(uint256 n, string memory errorMessage)
            internal
            pure
            returns (uint96)
        {
            require(n < 2**96, errorMessage);
            return uint96(n);
        }
    
        function add96(
            uint96 a,
            uint96 b,
            string memory errorMessage
        ) internal pure returns (uint96) {
            uint96 c = a + b;
            require(c >= a, errorMessage);
            return c;
        }
    
        function sub96(
            uint96 a,
            uint96 b,
            string memory errorMessage
        ) internal pure returns (uint96) {
            require(b <= a, errorMessage);
            return a - b;
        }
    
        function getChainId() internal view returns (uint256) {
            uint256 chainId;
            assembly {
                chainId := chainid()
            }
            return chainId;
        }
    }