ETH Price: $1,876.43 (+0.26%)

Transaction Decoder

Block:
21501173 at Dec-28-2024 01:10:47 PM +UTC
Transaction Fee:
0.000311595752116365 ETH $0.58
Gas Used:
66,565 Gas / 4.681074921 Gwei

Emitted Events:

257 DefaultCollateral.Transfer( from=[Sender] 0x81dcaf211b3a9c1e39dd175dc0404671c4667a14, to=0x0000000000000000000000000000000000000000, value=27878000000000000 )
258 FiatTokenProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x000000000000000000000000b26ff591f44b04e78de18f43b46f8b70c6676984, 0x00000000000000000000000081dcaf211b3a9c1e39dd175dc0404671c4667a14, 00000000000000000000000000000000000000000000000000630ae4af0c6000 )
259 DefaultCollateral.Withdraw( withdrawer=[Sender] 0x81dcaf211b3a9c1e39dd175dc0404671c4667a14, recipient=[Sender] 0x81dcaf211b3a9c1e39dd175dc0404671c4667a14, amount=27878000000000000 )

Account State Difference:

  Address   Before After State Difference Code
(Titan Builder)
14.825048882347153836 Eth14.825106559250736156 Eth0.00005767690358232
0x81DcaF21...1C4667a14
0.011579375464410836 Eth
Nonce: 9
0.011267779712294471 Eth
Nonce: 10
0.000311595752116365
0xB26ff591...0C6676984
0xBe989514...41Ea49704

Execution Trace

DefaultCollateral.withdraw( recipient=0x81DcaF211B3a9C1e39Dd175dc0404671C4667a14, amount=27878000000000000 )
  • DefaultCollateral.withdraw( recipient=0x81DcaF211B3a9C1e39Dd175dc0404671C4667a14, amount=27878000000000000 )
    • FiatTokenProxy.a9059cbb( )
      • StakedTokenV1.transfer( to=0x81DcaF211B3a9C1e39Dd175dc0404671C4667a14, value=27878000000000000 ) => ( True )
        File 1 of 4: DefaultCollateral
        // SPDX-License-Identifier: MIT
        pragma solidity 0.8.25;
        import {IDefaultCollateral} from "src/interfaces/defaultCollateral/IDefaultCollateral.sol";
        import {ICollateral} from "src/interfaces/ICollateral.sol";
        import {Permit2Lib} from "src/contracts/libraries/Permit2Lib.sol";
        import {ReentrancyGuardUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
        import {ERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
        import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
        import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
        import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
        contract DefaultCollateral is ERC20Upgradeable, ReentrancyGuardUpgradeable, IDefaultCollateral {
            using SafeERC20 for IERC20;
            using Permit2Lib for IERC20;
            uint8 private DECIMALS;
            /**
             * @inheritdoc ICollateral
             */
            address public asset;
            /**
             * @inheritdoc ICollateral
             */
            uint256 public totalRepaidDebt;
            /**
             * @inheritdoc ICollateral
             */
            mapping(address issuer => uint256 amount) public issuerRepaidDebt;
            /**
             * @inheritdoc ICollateral
             */
            mapping(address recipient => uint256 amount) public recipientRepaidDebt;
            /**
             * @inheritdoc ICollateral
             */
            mapping(address issuer => mapping(address recipient => uint256 amount)) public repaidDebt;
            /**
             * @inheritdoc ICollateral
             */
            uint256 public totalDebt;
            /**
             * @inheritdoc ICollateral
             */
            mapping(address issuer => uint256 amount) public issuerDebt;
            /**
             * @inheritdoc ICollateral
             */
            mapping(address recipient => uint256 amount) public recipientDebt;
            /**
             * @inheritdoc ICollateral
             */
            mapping(address issuer => mapping(address recipient => uint256 amount)) public debt;
            /**
             * @inheritdoc IDefaultCollateral
             */
            uint256 public limit;
            /**
             * @inheritdoc IDefaultCollateral
             */
            address public limitIncreaser;
            modifier onlyLimitIncreaser() {
                if (msg.sender != limitIncreaser) {
                    revert NotLimitIncreaser();
                }
                _;
            }
            constructor() {
                _disableInitializers();
            }
            function initialize(address asset_, uint256 initialLimit, address limitIncreaser_) external initializer {
                __ERC20_init(
                    string.concat("DefaultCollateral_", IERC20Metadata(asset_).name()),
                    string.concat("DC_", IERC20Metadata(asset_).symbol())
                );
                __ReentrancyGuard_init();
                asset = asset_;
                limit = initialLimit;
                limitIncreaser = limitIncreaser_;
                DECIMALS = IERC20Metadata(asset).decimals();
            }
            /**
             * @inheritdoc ERC20Upgradeable
             */
            function decimals() public view override returns (uint8) {
                return DECIMALS;
            }
            /**
             * @inheritdoc IDefaultCollateral
             */
            function deposit(address recipient, uint256 amount) public nonReentrant returns (uint256) {
                uint256 balanceBefore = IERC20(asset).balanceOf(address(this));
                IERC20(asset).transferFrom2(msg.sender, address(this), amount);
                amount = IERC20(asset).balanceOf(address(this)) - balanceBefore;
                if (amount == 0) {
                    revert InsufficientDeposit();
                }
                if (totalSupply() + amount > limit) {
                    revert ExceedsLimit();
                }
                _mint(recipient, amount);
                emit Deposit(msg.sender, recipient, amount);
                return amount;
            }
            /**
             * @inheritdoc IDefaultCollateral
             */
            function deposit(
                address recipient,
                uint256 amount,
                uint256 deadline,
                uint8 v,
                bytes32 r,
                bytes32 s
            ) external returns (uint256) {
                IERC20(asset).tryPermit2(msg.sender, address(this), amount, deadline, v, r, s);
                return deposit(recipient, amount);
            }
            /**
             * @inheritdoc IDefaultCollateral
             */
            function withdraw(address recipient, uint256 amount) external {
                if (amount == 0) {
                    revert InsufficientWithdraw();
                }
                _burn(msg.sender, amount);
                IERC20(asset).safeTransfer(recipient, amount);
                emit Withdraw(msg.sender, recipient, amount);
            }
            /**
             * @inheritdoc ICollateral
             */
            function issueDebt(address recipient, uint256 amount) external {
                if (amount == 0) {
                    revert InsufficientIssueDebt();
                }
                _burn(msg.sender, amount);
                emit IssueDebt(msg.sender, recipient, amount);
                totalRepaidDebt += amount;
                issuerRepaidDebt[msg.sender] += amount;
                recipientRepaidDebt[recipient] += amount;
                repaidDebt[msg.sender][recipient] += amount;
                IERC20(asset).safeTransfer(recipient, amount);
                emit RepayDebt(msg.sender, recipient, amount);
            }
            /**
             * @inheritdoc IDefaultCollateral
             */
            function increaseLimit(uint256 amount) external onlyLimitIncreaser {
                limit += amount;
                emit IncreaseLimit(amount);
            }
            /**
             * @inheritdoc IDefaultCollateral
             */
            function setLimitIncreaser(address limitIncreaser_) external onlyLimitIncreaser {
                limitIncreaser = limitIncreaser_;
                emit SetLimitIncreaser(limitIncreaser_);
            }
        }
        // SPDX-License-Identifier: MIT
        pragma solidity 0.8.25;
        import {ICollateral} from "src/interfaces/ICollateral.sol";
        interface IDefaultCollateral is ICollateral {
            error NotLimitIncreaser();
            error InsufficientDeposit();
            error ExceedsLimit();
            error InsufficientWithdraw();
            error InsufficientIssueDebt();
            /**
             * @notice Emmited when deposit happens.
             * @param depositor address of the depositor
             * @param recipient address of the collateral's recipient
             * @param amount amount of the collateral minted
             */
            event Deposit(address indexed depositor, address indexed recipient, uint256 amount);
            /**
             * @notice Emmited when withdrawal happens.
             * @param withdrawer address of the withdrawer
             * @param recipient address of the underlying asset's recipient
             * @param amount amount of the collateral burned
             */
            event Withdraw(address indexed withdrawer, address indexed recipient, uint256 amount);
            /**
             * @notice Emmited when limit is increased.
             * @param amount amount to increase the limit by
             */
            event IncreaseLimit(uint256 amount);
            /**
             * @notice Emmited when new limit increaser is set.
             * @param limitIncreaser address of the new limit increaser
             */
            event SetLimitIncreaser(address indexed limitIncreaser);
            /**
             * @notice Get a maximum possible collateral total supply.
             * @return maximum collateral total supply
             */
            function limit() external view returns (uint256);
            /**
             * @notice Get an address of the limit increaser.
             * @return address of the limit increaser
             */
            function limitIncreaser() external view returns (address);
            /**
             * @notice Deposit a given amount of the underlying asset, and mint the collateral to a particular recipient.
             * @param recipient address of the collateral's recipient
             * @param amount amount of the underlying asset
             * @return amount of the collateral minted
             */
            function deposit(address recipient, uint256 amount) external returns (uint256);
            /**
             * @notice Deposit a given amount of the underlying asset using a permit functionality, and mint the collateral to a particular recipient.
             * @param recipient address of the collateral's recipient
             * @param amount amount of the underlying asset
             * @param deadline timestamp of the signature's deadline
             * @param v v component of the signature
             * @param r r component of the signature
             * @param s s component of the signature
             * @return amount of the collateral minted
             */
            function deposit(
                address recipient,
                uint256 amount,
                uint256 deadline,
                uint8 v,
                bytes32 r,
                bytes32 s
            ) external returns (uint256);
            /**
             * @notice Withdraw a given amount of the underlying asset, and transfer it to a particular recipient.
             * @param recipient address of the underlying asset's recipient
             * @param amount amount of the underlying asset
             */
            function withdraw(address recipient, uint256 amount) external;
            /**
             * @notice Increase a limit of maximum collateral total supply.
             * @param amount amount to increase the limit by
             * @dev Called only by limitIncreaser.
             */
            function increaseLimit(uint256 amount) external;
            /**
             * @notice Set a new limit increaser.
             * @param limitIncreaser address of the new limit increaser
             * @dev Called only by limitIncreaser.
             */
            function setLimitIncreaser(address limitIncreaser) external;
        }
        // SPDX-License-Identifier: MIT
        pragma solidity 0.8.25;
        import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
        interface ICollateral is IERC20 {
            /**
             * @notice Emitted when debt is issued.
             * @param issuer address of the debt's issuer
             * @param recipient address that should receive the underlying asset
             * @param debtIssued amount of the debt issued
             */
            event IssueDebt(address indexed issuer, address indexed recipient, uint256 debtIssued);
            /**
             * @notice Emitted when debt is repaid.
             * @param issuer address of the debt's issuer
             * @param recipient address that received the underlying asset
             * @param debtRepaid amount of the debt repaid
             */
            event RepayDebt(address indexed issuer, address indexed recipient, uint256 debtRepaid);
            /**
             * @notice Get the collateral's underlying asset.
             * @return asset address of the underlying asset
             */
            function asset() external view returns (address);
            /**
             * @notice Get a total amount of repaid debt.
             * @return total repaid debt
             */
            function totalRepaidDebt() external view returns (uint256);
            /**
             * @notice Get an amount of repaid debt created by a particular issuer.
             * @param issuer address of the debt's issuer
             * @return particular issuer's repaid debt
             */
            function issuerRepaidDebt(address issuer) external view returns (uint256);
            /**
             * @notice Get an amount of repaid debt to a particular recipient.
             * @param recipient address that received the underlying asset
             * @return particular recipient's repaid debt
             */
            function recipientRepaidDebt(address recipient) external view returns (uint256);
            /**
             * @notice Get an amount of repaid debt for a particular issuer-recipient pair.
             * @param issuer address of the debt's issuer
             * @param recipient address that received the underlying asset
             * @return particular pair's repaid debt
             */
            function repaidDebt(address issuer, address recipient) external view returns (uint256);
            /**
             * @notice Get a total amount of debt.
             * @return total debt
             */
            function totalDebt() external view returns (uint256);
            /**
             * @notice Get a current debt created by a particular issuer.
             * @param issuer address of the debt's issuer
             * @return particular issuer's debt
             */
            function issuerDebt(address issuer) external view returns (uint256);
            /**
             * @notice Get a current debt to a particular recipient.
             * @param recipient address that should receive the underlying asset
             * @return particular recipient's debt
             */
            function recipientDebt(address recipient) external view returns (uint256);
            /**
             * @notice Get a current debt for a particular issuer-recipient pair.
             * @param issuer address of the debt's issuer
             * @param recipient address that should receive the underlying asset
             * @return particular pair's debt
             */
            function debt(address issuer, address recipient) external view returns (uint256);
            /**
             * @notice Burn a given amount of the collateral, and increase a debt of the underlying asset for the caller.
             * @param recipient address that should receive the underlying asset
             * @param amount amount of the collateral
             */
            function issueDebt(address recipient, uint256 amount) external;
        }
        // SPDX-License-Identifier: MIT
        pragma solidity ^0.8.17;
        import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
        import {IERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol";
        import {IDAIPermit} from "permit2/src/interfaces/IDAIPermit.sol";
        import {IAllowanceTransfer} from "permit2/src/interfaces/IAllowanceTransfer.sol";
        import {SafeCast160} from "permit2/src/libraries/SafeCast160.sol";
        /// @title Permit2Lib
        /// @notice Enables efficient transfers and EIP-2612/DAI
        /// permits for any token by falling back to Permit2.
        /// @author Modified from Uniswap (https://github.com/Uniswap/permit2/blob/main/src/libraries/Permit2Lib.sol)
        library Permit2Lib {
            using SafeCast160 for uint256;
            /*//////////////////////////////////////////////////////////////
                                        CONSTANTS
            //////////////////////////////////////////////////////////////*/
            /// @dev The unique EIP-712 domain domain separator for the DAI token contract.
            bytes32 internal constant DAI_DOMAIN_SEPARATOR = 0xdbb8cf42e1ecb028be3f3dbc922e1d878b963f411dc388ced501601c60f7c6f7;
            /// @dev The address for the WETH9 contract on Ethereum mainnet, encoded as a bytes32.
            bytes32 internal constant WETH9_ADDRESS = 0x000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2;
            /// @dev The address of the Permit2 contract the library will use.
            IAllowanceTransfer internal constant PERMIT2 =
                IAllowanceTransfer(address(0x000000000022D473030F116dDEE9F6B43aC78BA3));
            /// @notice Transfer a given amount of tokens from one user to another.
            /// @param token The token to transfer.
            /// @param from The user to transfer from.
            /// @param to The user to transfer to.
            /// @param amount The amount to transfer.
            function transferFrom2(IERC20 token, address from, address to, uint256 amount) internal {
                // Generate calldata for a standard transferFrom call.
                bytes memory inputData = abi.encodeCall(IERC20.transferFrom, (from, to, amount));
                bool success; // Call the token contract as normal, capturing whether it succeeded.
                assembly {
                    success :=
                        and(
                            // Set success to whether the call reverted, if not we check it either
                            // returned exactly 1 (can't just be non-zero data), or had no return data.
                            or(eq(mload(0), 1), iszero(returndatasize())),
                            // Counterintuitively, this call() must be positioned after the or() in the
                            // surrounding and() because and() evaluates its arguments from right to left.
                            // We use 0 and 32 to copy up to 32 bytes of return data into the first slot of scratch space.
                            call(gas(), token, 0, add(inputData, 32), mload(inputData), 0, 32)
                        )
                }
                // We'll fall back to using Permit2 if calling transferFrom on the token directly reverted.
                if (!success) PERMIT2.transferFrom(from, to, amount.toUint160(), address(token));
            }
            /*//////////////////////////////////////////////////////////////
                                      PERMIT LOGIC
            //////////////////////////////////////////////////////////////*/
            /// @notice Permit a user to spend a given amount of
            /// another user's tokens via native EIP-2612 permit if possible, falling
            /// back to Permit2 if native permit fails or is not implemented on the token.
            /// @param token The token to permit spending.
            /// @param owner The user to permit spending from.
            /// @param spender The user to permit spending to.
            /// @param amount The amount to permit spending.
            /// @param deadline  The timestamp after which the signature is no longer valid.
            /// @param v Must produce valid secp256k1 signature from the owner along with r and s.
            /// @param r Must produce valid secp256k1 signature from the owner along with v and s.
            /// @param s Must produce valid secp256k1 signature from the owner along with r and v.
            function tryPermit2(
                IERC20 token,
                address owner,
                address spender,
                uint256 amount,
                uint256 deadline,
                uint8 v,
                bytes32 r,
                bytes32 s
            ) internal {
                // Generate calldata for a call to DOMAIN_SEPARATOR on the token.
                bytes memory inputData = abi.encodeWithSelector(IERC20Permit.DOMAIN_SEPARATOR.selector);
                bool success; // Call the token contract as normal, capturing whether it succeeded.
                bytes32 domainSeparator; // If the call succeeded, we'll capture the return value here.
                assembly {
                    // If the token is WETH9, we know it doesn't have a DOMAIN_SEPARATOR, and we'll skip this step.
                    // We make sure to mask the token address as its higher order bits aren't guaranteed to be clean.
                    if iszero(eq(and(token, 0xffffffffffffffffffffffffffffffffffffffff), WETH9_ADDRESS)) {
                        success :=
                            and(
                                // Should resolve false if its not 32 bytes or its first word is 0.
                                and(iszero(iszero(mload(0))), eq(returndatasize(), 32)),
                                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                                // Counterintuitively, this call must be positioned second to the and() call in the
                                // surrounding and() call or else returndatasize() will be zero during the computation.
                                // We send a maximum of 5000 gas to prevent tokens with fallbacks from using a ton of gas.
                                // which should be plenty to allow tokens to fetch their DOMAIN_SEPARATOR from storage, etc.
                                staticcall(5000, token, add(inputData, 32), mload(inputData), 0, 32)
                            )
                        domainSeparator := mload(0) // Copy the return value into the domainSeparator variable.
                    }
                }
                // If the call to DOMAIN_SEPARATOR succeeded, try using permit on the token.
                if (success) {
                    // We'll use DAI's special permit if it's DOMAIN_SEPARATOR matches,
                    // otherwise we'll just encode a call to the standard permit function.
                    inputData = domainSeparator == DAI_DOMAIN_SEPARATOR
                        ? abi.encodeCall(
                            IDAIPermit.permit, (owner, spender, IERC20Permit(address(token)).nonces(owner), deadline, true, v, r, s)
                        )
                        : abi.encodeCall(IERC20Permit.permit, (owner, spender, amount, deadline, v, r, s));
                    assembly {
                        success := call(gas(), token, 0, add(inputData, 32), mload(inputData), 0, 0)
                    }
                }
                if (!success) {
                    // If the initial DOMAIN_SEPARATOR call on the token failed or a
                    // subsequent call to permit failed, fall back to using Permit2.
                    simplePermit2(token, owner, spender, amount, deadline, v, r, s);
                }
            }
            /// @notice Simple unlimited permit on the Permit2 contract.
            /// @param token The token to permit spending.
            /// @param owner The user to permit spending from.
            /// @param spender The user to permit spending to.
            /// @param amount The amount to permit spending.
            /// @param deadline  The timestamp after which the signature is no longer valid.
            /// @param v Must produce valid secp256k1 signature from the owner along with r and s.
            /// @param r Must produce valid secp256k1 signature from the owner along with v and s.
            /// @param s Must produce valid secp256k1 signature from the owner along with r and v.
            function simplePermit2(
                IERC20 token,
                address owner,
                address spender,
                uint256 amount,
                uint256 deadline,
                uint8 v,
                bytes32 r,
                bytes32 s
            ) internal {
                (,, uint48 nonce) = PERMIT2.allowance(owner, address(token), spender);
                try PERMIT2.permit(
                    owner,
                    IAllowanceTransfer.PermitSingle({
                        details: IAllowanceTransfer.PermitDetails({
                            token: address(token),
                            amount: amount.toUint160(),
                            // Use an unlimited expiration because it most
                            // closely mimics how a standard approval works.
                            expiration: type(uint48).max,
                            nonce: nonce
                        }),
                        spender: spender,
                        sigDeadline: deadline
                    }),
                    bytes.concat(r, s, bytes1(v))
                ) {} catch {}
            }
        }
        // SPDX-License-Identifier: MIT
        // OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol)
        pragma solidity ^0.8.20;
        import {Initializable} from "../proxy/utils/Initializable.sol";
        /**
         * @dev Contract module that helps prevent reentrant calls to a function.
         *
         * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
         * available, which can be applied to functions to make sure there are no nested
         * (reentrant) calls to them.
         *
         * Note that because there is a single `nonReentrant` guard, functions marked as
         * `nonReentrant` may not call one another. This can be worked around by making
         * those functions `private`, and then adding `external` `nonReentrant` entry
         * points to them.
         *
         * TIP: If you would like to learn more about reentrancy and alternative ways
         * to protect against it, check out our blog post
         * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
         */
        abstract contract ReentrancyGuardUpgradeable is Initializable {
            // 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;
            /// @custom:storage-location erc7201:openzeppelin.storage.ReentrancyGuard
            struct ReentrancyGuardStorage {
                uint256 _status;
            }
            // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ReentrancyGuard")) - 1)) & ~bytes32(uint256(0xff))
            bytes32 private constant ReentrancyGuardStorageLocation = 0x9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00;
            function _getReentrancyGuardStorage() private pure returns (ReentrancyGuardStorage storage $) {
                assembly {
                    $.slot := ReentrancyGuardStorageLocation
                }
            }
            /**
             * @dev Unauthorized reentrant call.
             */
            error ReentrancyGuardReentrantCall();
            function __ReentrancyGuard_init() internal onlyInitializing {
                __ReentrancyGuard_init_unchained();
            }
            function __ReentrancyGuard_init_unchained() internal onlyInitializing {
                ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
                $._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 {
                ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
                // On the first call to nonReentrant, _status will be NOT_ENTERED
                if ($._status == ENTERED) {
                    revert ReentrancyGuardReentrantCall();
                }
                // Any calls to nonReentrant after this point will fail
                $._status = ENTERED;
            }
            function _nonReentrantAfter() private {
                ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
                // By storing the original value once again, a refund is triggered (see
                // https://eips.ethereum.org/EIPS/eip-2200)
                $._status = NOT_ENTERED;
            }
            /**
             * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
             * `nonReentrant` function in the call stack.
             */
            function _reentrancyGuardEntered() internal view returns (bool) {
                ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
                return $._status == ENTERED;
            }
        }
        // SPDX-License-Identifier: MIT
        // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/ERC20.sol)
        pragma solidity ^0.8.20;
        import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
        import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
        import {ContextUpgradeable} from "../../utils/ContextUpgradeable.sol";
        import {IERC20Errors} from "@openzeppelin/contracts/interfaces/draft-IERC6093.sol";
        import {Initializable} from "../../proxy/utils/Initializable.sol";
        /**
         * @dev Implementation of the {IERC20} interface.
         *
         * This implementation is agnostic to the way tokens are created. This means
         * that a supply mechanism has to be added in a derived contract using {_mint}.
         *
         * TIP: For a detailed writeup see our guide
         * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
         * to implement supply mechanisms].
         *
         * The default value of {decimals} is 18. To change this, you should override
         * this function so it returns a different value.
         *
         * We have followed general OpenZeppelin Contracts guidelines: functions revert
         * instead returning `false` on failure. This behavior is nonetheless
         * conventional and does not conflict with the expectations of ERC20
         * applications.
         *
         * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
         * This allows applications to reconstruct the allowance for all accounts just
         * by listening to said events. Other implementations of the EIP may not emit
         * these events, as it isn't required by the specification.
         */
        abstract contract ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20, IERC20Metadata, IERC20Errors {
            /// @custom:storage-location erc7201:openzeppelin.storage.ERC20
            struct ERC20Storage {
                mapping(address account => uint256) _balances;
                mapping(address account => mapping(address spender => uint256)) _allowances;
                uint256 _totalSupply;
                string _name;
                string _symbol;
            }
            // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ERC20")) - 1)) & ~bytes32(uint256(0xff))
            bytes32 private constant ERC20StorageLocation = 0x52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace00;
            function _getERC20Storage() private pure returns (ERC20Storage storage $) {
                assembly {
                    $.slot := ERC20StorageLocation
                }
            }
            /**
             * @dev Sets the values for {name} and {symbol}.
             *
             * All two of these values are immutable: they can only be set once during
             * construction.
             */
            function __ERC20_init(string memory name_, string memory symbol_) internal onlyInitializing {
                __ERC20_init_unchained(name_, symbol_);
            }
            function __ERC20_init_unchained(string memory name_, string memory symbol_) internal onlyInitializing {
                ERC20Storage storage $ = _getERC20Storage();
                $._name = name_;
                $._symbol = symbol_;
            }
            /**
             * @dev Returns the name of the token.
             */
            function name() public view virtual returns (string memory) {
                ERC20Storage storage $ = _getERC20Storage();
                return $._name;
            }
            /**
             * @dev Returns the symbol of the token, usually a shorter version of the
             * name.
             */
            function symbol() public view virtual returns (string memory) {
                ERC20Storage storage $ = _getERC20Storage();
                return $._symbol;
            }
            /**
             * @dev Returns the number of decimals used to get its user representation.
             * For example, if `decimals` equals `2`, a balance of `505` tokens should
             * be displayed to a user as `5.05` (`505 / 10 ** 2`).
             *
             * Tokens usually opt for a value of 18, imitating the relationship between
             * Ether and Wei. This is the default value returned by this function, unless
             * it's overridden.
             *
             * NOTE: This information is only used for _display_ purposes: it in
             * no way affects any of the arithmetic of the contract, including
             * {IERC20-balanceOf} and {IERC20-transfer}.
             */
            function decimals() public view virtual returns (uint8) {
                return 18;
            }
            /**
             * @dev See {IERC20-totalSupply}.
             */
            function totalSupply() public view virtual returns (uint256) {
                ERC20Storage storage $ = _getERC20Storage();
                return $._totalSupply;
            }
            /**
             * @dev See {IERC20-balanceOf}.
             */
            function balanceOf(address account) public view virtual returns (uint256) {
                ERC20Storage storage $ = _getERC20Storage();
                return $._balances[account];
            }
            /**
             * @dev See {IERC20-transfer}.
             *
             * Requirements:
             *
             * - `to` cannot be the zero address.
             * - the caller must have a balance of at least `value`.
             */
            function transfer(address to, uint256 value) public virtual returns (bool) {
                address owner = _msgSender();
                _transfer(owner, to, value);
                return true;
            }
            /**
             * @dev See {IERC20-allowance}.
             */
            function allowance(address owner, address spender) public view virtual returns (uint256) {
                ERC20Storage storage $ = _getERC20Storage();
                return $._allowances[owner][spender];
            }
            /**
             * @dev See {IERC20-approve}.
             *
             * NOTE: If `value` is the maximum `uint256`, the allowance is not updated on
             * `transferFrom`. This is semantically equivalent to an infinite approval.
             *
             * Requirements:
             *
             * - `spender` cannot be the zero address.
             */
            function approve(address spender, uint256 value) public virtual returns (bool) {
                address owner = _msgSender();
                _approve(owner, spender, value);
                return true;
            }
            /**
             * @dev See {IERC20-transferFrom}.
             *
             * Emits an {Approval} event indicating the updated allowance. This is not
             * required by the EIP. See the note at the beginning of {ERC20}.
             *
             * NOTE: Does not update the allowance if the current allowance
             * is the maximum `uint256`.
             *
             * Requirements:
             *
             * - `from` and `to` cannot be the zero address.
             * - `from` must have a balance of at least `value`.
             * - the caller must have allowance for ``from``'s tokens of at least
             * `value`.
             */
            function transferFrom(address from, address to, uint256 value) public virtual returns (bool) {
                address spender = _msgSender();
                _spendAllowance(from, spender, value);
                _transfer(from, to, value);
                return true;
            }
            /**
             * @dev Moves a `value` amount of tokens from `from` to `to`.
             *
             * This internal function is equivalent to {transfer}, and can be used to
             * e.g. implement automatic token fees, slashing mechanisms, etc.
             *
             * Emits a {Transfer} event.
             *
             * NOTE: This function is not virtual, {_update} should be overridden instead.
             */
            function _transfer(address from, address to, uint256 value) internal {
                if (from == address(0)) {
                    revert ERC20InvalidSender(address(0));
                }
                if (to == address(0)) {
                    revert ERC20InvalidReceiver(address(0));
                }
                _update(from, to, value);
            }
            /**
             * @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from`
             * (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding
             * this function.
             *
             * Emits a {Transfer} event.
             */
            function _update(address from, address to, uint256 value) internal virtual {
                ERC20Storage storage $ = _getERC20Storage();
                if (from == address(0)) {
                    // Overflow check required: The rest of the code assumes that totalSupply never overflows
                    $._totalSupply += value;
                } else {
                    uint256 fromBalance = $._balances[from];
                    if (fromBalance < value) {
                        revert ERC20InsufficientBalance(from, fromBalance, value);
                    }
                    unchecked {
                        // Overflow not possible: value <= fromBalance <= totalSupply.
                        $._balances[from] = fromBalance - value;
                    }
                }
                if (to == address(0)) {
                    unchecked {
                        // Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply.
                        $._totalSupply -= value;
                    }
                } else {
                    unchecked {
                        // Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.
                        $._balances[to] += value;
                    }
                }
                emit Transfer(from, to, value);
            }
            /**
             * @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0).
             * Relies on the `_update` mechanism
             *
             * Emits a {Transfer} event with `from` set to the zero address.
             *
             * NOTE: This function is not virtual, {_update} should be overridden instead.
             */
            function _mint(address account, uint256 value) internal {
                if (account == address(0)) {
                    revert ERC20InvalidReceiver(address(0));
                }
                _update(address(0), account, value);
            }
            /**
             * @dev Destroys a `value` amount of tokens from `account`, lowering the total supply.
             * Relies on the `_update` mechanism.
             *
             * Emits a {Transfer} event with `to` set to the zero address.
             *
             * NOTE: This function is not virtual, {_update} should be overridden instead
             */
            function _burn(address account, uint256 value) internal {
                if (account == address(0)) {
                    revert ERC20InvalidSender(address(0));
                }
                _update(account, address(0), value);
            }
            /**
             * @dev Sets `value` as the allowance of `spender` over the `owner` s tokens.
             *
             * This internal function is equivalent to `approve`, and can be used to
             * e.g. set automatic allowances for certain subsystems, etc.
             *
             * Emits an {Approval} event.
             *
             * Requirements:
             *
             * - `owner` cannot be the zero address.
             * - `spender` cannot be the zero address.
             *
             * Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
             */
            function _approve(address owner, address spender, uint256 value) internal {
                _approve(owner, spender, value, true);
            }
            /**
             * @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event.
             *
             * By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by
             * `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any
             * `Approval` event during `transferFrom` operations.
             *
             * Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to
             * true using the following override:
             * ```
             * function _approve(address owner, address spender, uint256 value, bool) internal virtual override {
             *     super._approve(owner, spender, value, true);
             * }
             * ```
             *
             * Requirements are the same as {_approve}.
             */
            function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {
                ERC20Storage storage $ = _getERC20Storage();
                if (owner == address(0)) {
                    revert ERC20InvalidApprover(address(0));
                }
                if (spender == address(0)) {
                    revert ERC20InvalidSpender(address(0));
                }
                $._allowances[owner][spender] = value;
                if (emitEvent) {
                    emit Approval(owner, spender, value);
                }
            }
            /**
             * @dev Updates `owner` s allowance for `spender` based on spent `value`.
             *
             * Does not update the allowance value in case of infinite allowance.
             * Revert if not enough allowance is available.
             *
             * Does not emit an {Approval} event.
             */
            function _spendAllowance(address owner, address spender, uint256 value) internal virtual {
                uint256 currentAllowance = allowance(owner, spender);
                if (currentAllowance != type(uint256).max) {
                    if (currentAllowance < value) {
                        revert ERC20InsufficientAllowance(spender, currentAllowance, value);
                    }
                    unchecked {
                        _approve(owner, spender, currentAllowance - value, false);
                    }
                }
            }
        }
        // SPDX-License-Identifier: MIT
        // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)
        pragma solidity ^0.8.20;
        /**
         * @dev Interface of the ERC20 standard as defined in the EIP.
         */
        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 value of tokens in existence.
             */
            function totalSupply() external view returns (uint256);
            /**
             * @dev Returns the value of tokens owned by `account`.
             */
            function balanceOf(address account) external view returns (uint256);
            /**
             * @dev Moves a `value` amount of 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 value) 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 a `value` amount of tokens 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 value) external returns (bool);
            /**
             * @dev Moves a `value` amount of tokens from `from` to `to` using the
             * allowance mechanism. `value` 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 value) external returns (bool);
        }
        // SPDX-License-Identifier: MIT
        // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Metadata.sol)
        pragma solidity ^0.8.20;
        import {IERC20} from "../IERC20.sol";
        /**
         * @dev Interface for the optional metadata functions from the ERC20 standard.
         */
        interface IERC20Metadata is IERC20 {
            /**
             * @dev Returns the name of the token.
             */
            function name() external view returns (string memory);
            /**
             * @dev Returns the symbol of the token.
             */
            function symbol() external view returns (string memory);
            /**
             * @dev Returns the decimals places of the token.
             */
            function decimals() external view returns (uint8);
        }
        // SPDX-License-Identifier: MIT
        // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)
        pragma solidity ^0.8.20;
        import {IERC20} from "../IERC20.sol";
        import {IERC20Permit} from "../extensions/IERC20Permit.sol";
        import {Address} from "../../../utils/Address.sol";
        /**
         * @title SafeERC20
         * @dev Wrappers around ERC20 operations that throw on failure (when the token
         * contract returns false). Tokens that return no value (and instead revert or
         * throw on failure) are also supported, non-reverting calls are assumed to be
         * successful.
         * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
         * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
         */
        library SafeERC20 {
            using Address for address;
            /**
             * @dev An operation with an ERC20 token failed.
             */
            error SafeERC20FailedOperation(address token);
            /**
             * @dev Indicates a failed `decreaseAllowance` request.
             */
            error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
            /**
             * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
             * non-reverting calls are assumed to be successful.
             */
            function safeTransfer(IERC20 token, address to, uint256 value) internal {
                _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
            }
            /**
             * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
             * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
             */
            function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
                _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
            }
            /**
             * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
             * non-reverting calls are assumed to be successful.
             */
            function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
                uint256 oldAllowance = token.allowance(address(this), spender);
                forceApprove(token, spender, oldAllowance + value);
            }
            /**
             * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
             * value, non-reverting calls are assumed to be successful.
             */
            function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
                unchecked {
                    uint256 currentAllowance = token.allowance(address(this), spender);
                    if (currentAllowance < requestedDecrease) {
                        revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
                    }
                    forceApprove(token, spender, currentAllowance - requestedDecrease);
                }
            }
            /**
             * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
             * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
             * to be set to zero before setting it to a non-zero value, such as USDT.
             */
            function forceApprove(IERC20 token, address spender, uint256 value) internal {
                bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
                if (!_callOptionalReturnBool(token, approvalCall)) {
                    _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
                    _callOptionalReturn(token, approvalCall);
                }
            }
            /**
             * @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);
                if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
                    revert SafeERC20FailedOperation(address(token));
                }
            }
            /**
             * @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).
             *
             * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
             */
            function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
                // 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 cannot use {Address-functionCall} here since this should return false
                // and not revert is the subcall reverts.
                (bool success, bytes memory returndata) = address(token).call(data);
                return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
            }
        }
        // SPDX-License-Identifier: MIT
        // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)
        pragma solidity ^0.8.20;
        /**
         * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
         * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
         *
         * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
         * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
         * need to send a transaction, and thus is not required to hold Ether at all.
         *
         * ==== Security Considerations
         *
         * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
         * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
         * considered as an intention to spend the allowance in any specific way. The second is that because permits have
         * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
         * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
         * generally recommended is:
         *
         * ```solidity
         * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
         *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
         *     doThing(..., value);
         * }
         *
         * function doThing(..., uint256 value) public {
         *     token.safeTransferFrom(msg.sender, address(this), value);
         *     ...
         * }
         * ```
         *
         * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
         * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
         * {SafeERC20-safeTransferFrom}).
         *
         * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
         * contracts should have entry points that don't rely on permit.
         */
        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].
             *
             * CAUTION: See Security Considerations above.
             */
            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);
        }
        // SPDX-License-Identifier: MIT
        pragma solidity ^0.8.0;
        interface IDAIPermit {
            /// @param holder The address of the token owner.
            /// @param spender The address of the token spender.
            /// @param nonce The owner's nonce, increases at each call to permit.
            /// @param expiry The timestamp at which the permit is no longer valid.
            /// @param allowed Boolean that sets approval amount, true for type(uint256).max and false for 0.
            /// @param v Must produce valid secp256k1 signature from the owner along with r and s.
            /// @param r Must produce valid secp256k1 signature from the owner along with v and s.
            /// @param s Must produce valid secp256k1 signature from the owner along with r and v.
            function permit(
                address holder,
                address spender,
                uint256 nonce,
                uint256 expiry,
                bool allowed,
                uint8 v,
                bytes32 r,
                bytes32 s
            ) external;
        }
        // SPDX-License-Identifier: MIT
        pragma solidity ^0.8.0;
        import {IEIP712} from "./IEIP712.sol";
        /// @title AllowanceTransfer
        /// @notice Handles ERC20 token permissions through signature based allowance setting and ERC20 token transfers by checking allowed amounts
        /// @dev Requires user's token approval on the Permit2 contract
        interface IAllowanceTransfer is IEIP712 {
            /// @notice Thrown when an allowance on a token has expired.
            /// @param deadline The timestamp at which the allowed amount is no longer valid
            error AllowanceExpired(uint256 deadline);
            /// @notice Thrown when an allowance on a token has been depleted.
            /// @param amount The maximum amount allowed
            error InsufficientAllowance(uint256 amount);
            /// @notice Thrown when too many nonces are invalidated.
            error ExcessiveInvalidation();
            /// @notice Emits an event when the owner successfully invalidates an ordered nonce.
            event NonceInvalidation(
                address indexed owner, address indexed token, address indexed spender, uint48 newNonce, uint48 oldNonce
            );
            /// @notice Emits an event when the owner successfully sets permissions on a token for the spender.
            event Approval(
                address indexed owner, address indexed token, address indexed spender, uint160 amount, uint48 expiration
            );
            /// @notice Emits an event when the owner successfully sets permissions using a permit signature on a token for the spender.
            event Permit(
                address indexed owner,
                address indexed token,
                address indexed spender,
                uint160 amount,
                uint48 expiration,
                uint48 nonce
            );
            /// @notice Emits an event when the owner sets the allowance back to 0 with the lockdown function.
            event Lockdown(address indexed owner, address token, address spender);
            /// @notice The permit data for a token
            struct PermitDetails {
                // ERC20 token address
                address token;
                // the maximum amount allowed to spend
                uint160 amount;
                // timestamp at which a spender's token allowances become invalid
                uint48 expiration;
                // an incrementing value indexed per owner,token,and spender for each signature
                uint48 nonce;
            }
            /// @notice The permit message signed for a single token allowance
            struct PermitSingle {
                // the permit data for a single token alownce
                PermitDetails details;
                // address permissioned on the allowed tokens
                address spender;
                // deadline on the permit signature
                uint256 sigDeadline;
            }
            /// @notice The permit message signed for multiple token allowances
            struct PermitBatch {
                // the permit data for multiple token allowances
                PermitDetails[] details;
                // address permissioned on the allowed tokens
                address spender;
                // deadline on the permit signature
                uint256 sigDeadline;
            }
            /// @notice The saved permissions
            /// @dev This info is saved per owner, per token, per spender and all signed over in the permit message
            /// @dev Setting amount to type(uint160).max sets an unlimited approval
            struct PackedAllowance {
                // amount allowed
                uint160 amount;
                // permission expiry
                uint48 expiration;
                // an incrementing value indexed per owner,token,and spender for each signature
                uint48 nonce;
            }
            /// @notice A token spender pair.
            struct TokenSpenderPair {
                // the token the spender is approved
                address token;
                // the spender address
                address spender;
            }
            /// @notice Details for a token transfer.
            struct AllowanceTransferDetails {
                // the owner of the token
                address from;
                // the recipient of the token
                address to;
                // the amount of the token
                uint160 amount;
                // the token to be transferred
                address token;
            }
            /// @notice A mapping from owner address to token address to spender address to PackedAllowance struct, which contains details and conditions of the approval.
            /// @notice The mapping is indexed in the above order see: allowance[ownerAddress][tokenAddress][spenderAddress]
            /// @dev The packed slot holds the allowed amount, expiration at which the allowed amount is no longer valid, and current nonce thats updated on any signature based approvals.
            function allowance(address user, address token, address spender)
                external
                view
                returns (uint160 amount, uint48 expiration, uint48 nonce);
            /// @notice Approves the spender to use up to amount of the specified token up until the expiration
            /// @param token The token to approve
            /// @param spender The spender address to approve
            /// @param amount The approved amount of the token
            /// @param expiration The timestamp at which the approval is no longer valid
            /// @dev The packed allowance also holds a nonce, which will stay unchanged in approve
            /// @dev Setting amount to type(uint160).max sets an unlimited approval
            function approve(address token, address spender, uint160 amount, uint48 expiration) external;
            /// @notice Permit a spender to a given amount of the owners token via the owner's EIP-712 signature
            /// @dev May fail if the owner's nonce was invalidated in-flight by invalidateNonce
            /// @param owner The owner of the tokens being approved
            /// @param permitSingle Data signed over by the owner specifying the terms of approval
            /// @param signature The owner's signature over the permit data
            function permit(address owner, PermitSingle memory permitSingle, bytes calldata signature) external;
            /// @notice Permit a spender to the signed amounts of the owners tokens via the owner's EIP-712 signature
            /// @dev May fail if the owner's nonce was invalidated in-flight by invalidateNonce
            /// @param owner The owner of the tokens being approved
            /// @param permitBatch Data signed over by the owner specifying the terms of approval
            /// @param signature The owner's signature over the permit data
            function permit(address owner, PermitBatch memory permitBatch, bytes calldata signature) external;
            /// @notice Transfer approved tokens from one address to another
            /// @param from The address to transfer from
            /// @param to The address of the recipient
            /// @param amount The amount of the token to transfer
            /// @param token The token address to transfer
            /// @dev Requires the from address to have approved at least the desired amount
            /// of tokens to msg.sender.
            function transferFrom(address from, address to, uint160 amount, address token) external;
            /// @notice Transfer approved tokens in a batch
            /// @param transferDetails Array of owners, recipients, amounts, and tokens for the transfers
            /// @dev Requires the from addresses to have approved at least the desired amount
            /// of tokens to msg.sender.
            function transferFrom(AllowanceTransferDetails[] calldata transferDetails) external;
            /// @notice Enables performing a "lockdown" of the sender's Permit2 identity
            /// by batch revoking approvals
            /// @param approvals Array of approvals to revoke.
            function lockdown(TokenSpenderPair[] calldata approvals) external;
            /// @notice Invalidate nonces for a given (token, spender) pair
            /// @param token The token to invalidate nonces for
            /// @param spender The spender to invalidate nonces for
            /// @param newNonce The new nonce to set. Invalidates all nonces less than it.
            /// @dev Can't invalidate more than 2**16 nonces per transaction.
            function invalidateNonces(address token, address spender, uint48 newNonce) external;
        }
        // SPDX-License-Identifier: MIT
        pragma solidity ^0.8.17;
        library SafeCast160 {
            /// @notice Thrown when a valude greater than type(uint160).max is cast to uint160
            error UnsafeCast();
            /// @notice Safely casts uint256 to uint160
            /// @param value The uint256 to be cast
            function toUint160(uint256 value) internal pure returns (uint160) {
                if (value > type(uint160).max) revert UnsafeCast();
                return uint160(value);
            }
        }
        // SPDX-License-Identifier: MIT
        // OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol)
        pragma solidity ^0.8.20;
        /**
         * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
         * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
         * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
         * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
         *
         * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
         * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
         * case an upgrade adds a module that needs to be initialized.
         *
         * For example:
         *
         * [.hljs-theme-light.nopadding]
         * ```solidity
         * contract MyToken is ERC20Upgradeable {
         *     function initialize() initializer public {
         *         __ERC20_init("MyToken", "MTK");
         *     }
         * }
         *
         * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
         *     function initializeV2() reinitializer(2) public {
         *         __ERC20Permit_init("MyToken");
         *     }
         * }
         * ```
         *
         * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
         * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
         *
         * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
         * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
         *
         * [CAUTION]
         * ====
         * Avoid leaving a contract uninitialized.
         *
         * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
         * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
         * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
         *
         * [.hljs-theme-light.nopadding]
         * ```
         * /// @custom:oz-upgrades-unsafe-allow constructor
         * constructor() {
         *     _disableInitializers();
         * }
         * ```
         * ====
         */
        abstract contract Initializable {
            /**
             * @dev Storage of the initializable contract.
             *
             * It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
             * when using with upgradeable contracts.
             *
             * @custom:storage-location erc7201:openzeppelin.storage.Initializable
             */
            struct InitializableStorage {
                /**
                 * @dev Indicates that the contract has been initialized.
                 */
                uint64 _initialized;
                /**
                 * @dev Indicates that the contract is in the process of being initialized.
                 */
                bool _initializing;
            }
            // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
            bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;
            /**
             * @dev The contract is already initialized.
             */
            error InvalidInitialization();
            /**
             * @dev The contract is not initializing.
             */
            error NotInitializing();
            /**
             * @dev Triggered when the contract has been initialized or reinitialized.
             */
            event Initialized(uint64 version);
            /**
             * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
             * `onlyInitializing` functions can be used to initialize parent contracts.
             *
             * Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
             * number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
             * production.
             *
             * Emits an {Initialized} event.
             */
            modifier initializer() {
                // solhint-disable-next-line var-name-mixedcase
                InitializableStorage storage $ = _getInitializableStorage();
                // Cache values to avoid duplicated sloads
                bool isTopLevelCall = !$._initializing;
                uint64 initialized = $._initialized;
                // Allowed calls:
                // - initialSetup: the contract is not in the initializing state and no previous version was
                //                 initialized
                // - construction: the contract is initialized at version 1 (no reininitialization) and the
                //                 current contract is just being deployed
                bool initialSetup = initialized == 0 && isTopLevelCall;
                bool construction = initialized == 1 && address(this).code.length == 0;
                if (!initialSetup && !construction) {
                    revert InvalidInitialization();
                }
                $._initialized = 1;
                if (isTopLevelCall) {
                    $._initializing = true;
                }
                _;
                if (isTopLevelCall) {
                    $._initializing = false;
                    emit Initialized(1);
                }
            }
            /**
             * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
             * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
             * used to initialize parent contracts.
             *
             * A reinitializer may be used after the original initialization step. This is essential to configure modules that
             * are added through upgrades and that require initialization.
             *
             * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
             * cannot be nested. If one is invoked in the context of another, execution will revert.
             *
             * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
             * a contract, executing them in the right order is up to the developer or operator.
             *
             * WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.
             *
             * Emits an {Initialized} event.
             */
            modifier reinitializer(uint64 version) {
                // solhint-disable-next-line var-name-mixedcase
                InitializableStorage storage $ = _getInitializableStorage();
                if ($._initializing || $._initialized >= version) {
                    revert InvalidInitialization();
                }
                $._initialized = version;
                $._initializing = true;
                _;
                $._initializing = false;
                emit Initialized(version);
            }
            /**
             * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
             * {initializer} and {reinitializer} modifiers, directly or indirectly.
             */
            modifier onlyInitializing() {
                _checkInitializing();
                _;
            }
            /**
             * @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
             */
            function _checkInitializing() internal view virtual {
                if (!_isInitializing()) {
                    revert NotInitializing();
                }
            }
            /**
             * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
             * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
             * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
             * through proxies.
             *
             * Emits an {Initialized} event the first time it is successfully executed.
             */
            function _disableInitializers() internal virtual {
                // solhint-disable-next-line var-name-mixedcase
                InitializableStorage storage $ = _getInitializableStorage();
                if ($._initializing) {
                    revert InvalidInitialization();
                }
                if ($._initialized != type(uint64).max) {
                    $._initialized = type(uint64).max;
                    emit Initialized(type(uint64).max);
                }
            }
            /**
             * @dev Returns the highest version that has been initialized. See {reinitializer}.
             */
            function _getInitializedVersion() internal view returns (uint64) {
                return _getInitializableStorage()._initialized;
            }
            /**
             * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
             */
            function _isInitializing() internal view returns (bool) {
                return _getInitializableStorage()._initializing;
            }
            /**
             * @dev Returns a pointer to the storage namespace.
             */
            // solhint-disable-next-line var-name-mixedcase
            function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
                assembly {
                    $.slot := INITIALIZABLE_STORAGE
                }
            }
        }
        // SPDX-License-Identifier: MIT
        // OpenZeppelin Contracts (last updated v5.0.0) (utils/Context.sol)
        pragma solidity ^0.8.20;
        import {Initializable} from "../proxy/utils/Initializable.sol";
        /**
         * @dev Provides information about the current execution context, including the
         * sender of the transaction and its data. While these are generally available
         * via msg.sender and msg.data, they should not be accessed in such a direct
         * manner, since when dealing with meta-transactions the account sending and
         * paying for execution may not be the actual sender (as far as an application
         * is concerned).
         *
         * This contract is only required for intermediate, library-like contracts.
         */
        abstract contract ContextUpgradeable is Initializable {
            function __Context_init() internal onlyInitializing {
            }
            function __Context_init_unchained() internal onlyInitializing {
            }
            function _msgSender() internal view virtual returns (address) {
                return msg.sender;
            }
            function _msgData() internal view virtual returns (bytes calldata) {
                return msg.data;
            }
        }
        // SPDX-License-Identifier: MIT
        // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC6093.sol)
        pragma solidity ^0.8.20;
        /**
         * @dev Standard ERC20 Errors
         * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC20 tokens.
         */
        interface IERC20Errors {
            /**
             * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
             * @param sender Address whose tokens are being transferred.
             * @param balance Current balance for the interacting account.
             * @param needed Minimum amount required to perform a transfer.
             */
            error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);
            /**
             * @dev Indicates a failure with the token `sender`. Used in transfers.
             * @param sender Address whose tokens are being transferred.
             */
            error ERC20InvalidSender(address sender);
            /**
             * @dev Indicates a failure with the token `receiver`. Used in transfers.
             * @param receiver Address to which tokens are being transferred.
             */
            error ERC20InvalidReceiver(address receiver);
            /**
             * @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.
             * @param spender Address that may be allowed to operate on tokens without being their owner.
             * @param allowance Amount of tokens a `spender` is allowed to operate with.
             * @param needed Minimum amount required to perform a transfer.
             */
            error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);
            /**
             * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
             * @param approver Address initiating an approval operation.
             */
            error ERC20InvalidApprover(address approver);
            /**
             * @dev Indicates a failure with the `spender` to be approved. Used in approvals.
             * @param spender Address that may be allowed to operate on tokens without being their owner.
             */
            error ERC20InvalidSpender(address spender);
        }
        /**
         * @dev Standard ERC721 Errors
         * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC721 tokens.
         */
        interface IERC721Errors {
            /**
             * @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in EIP-20.
             * Used in balance queries.
             * @param owner Address of the current owner of a token.
             */
            error ERC721InvalidOwner(address owner);
            /**
             * @dev Indicates a `tokenId` whose `owner` is the zero address.
             * @param tokenId Identifier number of a token.
             */
            error ERC721NonexistentToken(uint256 tokenId);
            /**
             * @dev Indicates an error related to the ownership over a particular token. Used in transfers.
             * @param sender Address whose tokens are being transferred.
             * @param tokenId Identifier number of a token.
             * @param owner Address of the current owner of a token.
             */
            error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);
            /**
             * @dev Indicates a failure with the token `sender`. Used in transfers.
             * @param sender Address whose tokens are being transferred.
             */
            error ERC721InvalidSender(address sender);
            /**
             * @dev Indicates a failure with the token `receiver`. Used in transfers.
             * @param receiver Address to which tokens are being transferred.
             */
            error ERC721InvalidReceiver(address receiver);
            /**
             * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
             * @param operator Address that may be allowed to operate on tokens without being their owner.
             * @param tokenId Identifier number of a token.
             */
            error ERC721InsufficientApproval(address operator, uint256 tokenId);
            /**
             * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
             * @param approver Address initiating an approval operation.
             */
            error ERC721InvalidApprover(address approver);
            /**
             * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
             * @param operator Address that may be allowed to operate on tokens without being their owner.
             */
            error ERC721InvalidOperator(address operator);
        }
        /**
         * @dev Standard ERC1155 Errors
         * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC1155 tokens.
         */
        interface IERC1155Errors {
            /**
             * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
             * @param sender Address whose tokens are being transferred.
             * @param balance Current balance for the interacting account.
             * @param needed Minimum amount required to perform a transfer.
             * @param tokenId Identifier number of a token.
             */
            error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);
            /**
             * @dev Indicates a failure with the token `sender`. Used in transfers.
             * @param sender Address whose tokens are being transferred.
             */
            error ERC1155InvalidSender(address sender);
            /**
             * @dev Indicates a failure with the token `receiver`. Used in transfers.
             * @param receiver Address to which tokens are being transferred.
             */
            error ERC1155InvalidReceiver(address receiver);
            /**
             * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
             * @param operator Address that may be allowed to operate on tokens without being their owner.
             * @param owner Address of the current owner of a token.
             */
            error ERC1155MissingApprovalForAll(address operator, address owner);
            /**
             * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
             * @param approver Address initiating an approval operation.
             */
            error ERC1155InvalidApprover(address approver);
            /**
             * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
             * @param operator Address that may be allowed to operate on tokens without being their owner.
             */
            error ERC1155InvalidOperator(address operator);
            /**
             * @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.
             * Used in batch transfers.
             * @param idsLength Length of the array of token identifiers
             * @param valuesLength Length of the array of token amounts
             */
            error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
        }
        // SPDX-License-Identifier: MIT
        // OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)
        pragma solidity ^0.8.20;
        /**
         * @dev Collection of functions related to the address type
         */
        library Address {
            /**
             * @dev The ETH balance of the account is not enough to perform the operation.
             */
            error AddressInsufficientBalance(address account);
            /**
             * @dev There's no code at `target` (it is not a contract).
             */
            error AddressEmptyCode(address target);
            /**
             * @dev A call to an address target failed. The target may have reverted.
             */
            error FailedInnerCall();
            /**
             * @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://consensys.net/diligence/blog/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.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
             */
            function sendValue(address payable recipient, uint256 amount) internal {
                if (address(this).balance < amount) {
                    revert AddressInsufficientBalance(address(this));
                }
                (bool success, ) = recipient.call{value: amount}("");
                if (!success) {
                    revert FailedInnerCall();
                }
            }
            /**
             * @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 or custom error, it is bubbled
             * up by this function (like regular Solidity function calls). However, if
             * the call reverted with no returned reason, this function reverts with a
             * {FailedInnerCall} error.
             *
             * 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.
             */
            function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                return functionCallWithValue(target, data, 0);
            }
            /**
             * @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`.
             */
            function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
                if (address(this).balance < value) {
                    revert AddressInsufficientBalance(address(this));
                }
                (bool success, bytes memory returndata) = target.call{value: value}(data);
                return verifyCallResultFromTarget(target, success, returndata);
            }
            /**
             * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
             * but performing a static call.
             */
            function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                (bool success, bytes memory returndata) = target.staticcall(data);
                return verifyCallResultFromTarget(target, success, returndata);
            }
            /**
             * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
             * but performing a delegate call.
             */
            function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                (bool success, bytes memory returndata) = target.delegatecall(data);
                return verifyCallResultFromTarget(target, success, returndata);
            }
            /**
             * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
             * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
             * unsuccessful call.
             */
            function verifyCallResultFromTarget(
                address target,
                bool success,
                bytes memory returndata
            ) internal view returns (bytes memory) {
                if (!success) {
                    _revert(returndata);
                } else {
                    // only check if target is a contract if the call was successful and the return data is empty
                    // otherwise we already know that it was a contract
                    if (returndata.length == 0 && target.code.length == 0) {
                        revert AddressEmptyCode(target);
                    }
                    return returndata;
                }
            }
            /**
             * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
             * revert reason or with a default {FailedInnerCall} error.
             */
            function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
                if (!success) {
                    _revert(returndata);
                } else {
                    return returndata;
                }
            }
            /**
             * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
             */
            function _revert(bytes memory returndata) 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 FailedInnerCall();
                }
            }
        }
        // SPDX-License-Identifier: MIT
        pragma solidity ^0.8.0;
        interface IEIP712 {
            function DOMAIN_SEPARATOR() external view returns (bytes32);
        }
        

        File 2 of 4: FiatTokenProxy
        /**
         * SPDX-License-Identifier: MIT
         *
         * Copyright (c) 2018-2020 CENTRE SECZ
         *
         * Permission is hereby granted, free of charge, to any person obtaining a copy
         * of this software and associated documentation files (the "Software"), to deal
         * in the Software without restriction, including without limitation the rights
         * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
         * copies of the Software, and to permit persons to whom the Software is
         * furnished to do so, subject to the following conditions:
         *
         * The above copyright notice and this permission notice shall be included in
         * copies or substantial portions of the Software.
         *
         * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
         * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
         * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
         * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
         * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
         * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
         * SOFTWARE.
         */
        pragma solidity 0.6.12;
        import {
            AdminUpgradeabilityProxy
        } from "../upgradeability/AdminUpgradeabilityProxy.sol";
        /**
         * @title FiatTokenProxy
         * @dev This contract proxies FiatToken calls and enables FiatToken upgrades
         */
        contract FiatTokenProxy is AdminUpgradeabilityProxy {
            constructor(address implementationContract)
                public
                AdminUpgradeabilityProxy(implementationContract)
            {}
        }
        /**
         * SPDX-License-Identifier: MIT
         *
         * Copyright (c) 2018 zOS Global Limited.
         *
         * Permission is hereby granted, free of charge, to any person obtaining a copy
         * of this software and associated documentation files (the "Software"), to deal
         * in the Software without restriction, including without limitation the rights
         * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
         * copies of the Software, and to permit persons to whom the Software is
         * furnished to do so, subject to the following conditions:
         *
         * The above copyright notice and this permission notice shall be included in
         * copies or substantial portions of the Software.
         *
         * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
         * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
         * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
         * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
         * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
         * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
         * SOFTWARE.
         */
        pragma solidity 0.6.12;
        import { UpgradeabilityProxy } from "./UpgradeabilityProxy.sol";
        /**
         * @notice This contract combines an upgradeability proxy with an authorization
         * mechanism for administrative tasks.
         * @dev Forked from https://github.com/zeppelinos/zos-lib/blob/8a16ef3ad17ec7430e3a9d2b5e3f39b8204f8c8d/contracts/upgradeability/AdminUpgradeabilityProxy.sol
         * Modifications:
         * 1. Reformat, conform to Solidity 0.6 syntax, and add error messages (5/13/20)
         * 2. Remove ifAdmin modifier from admin() and implementation() (5/13/20)
         */
        contract AdminUpgradeabilityProxy is UpgradeabilityProxy {
            /**
             * @dev Emitted when the administration has been transferred.
             * @param previousAdmin Address of the previous admin.
             * @param newAdmin Address of the new admin.
             */
            event AdminChanged(address previousAdmin, address newAdmin);
            /**
             * @dev Storage slot with the admin of the contract.
             * This is the keccak-256 hash of "org.zeppelinos.proxy.admin", and is
             * validated in the constructor.
             */
            bytes32
                private constant ADMIN_SLOT = 0x10d6a54a4754c8869d6886b5f5d7fbfa5b4522237ea5c60d11bc4e7a1ff9390b;
            /**
             * @dev Modifier to check whether the `msg.sender` is the admin.
             * If it is, it will run the function. Otherwise, it will delegate the call
             * to the implementation.
             */
            modifier ifAdmin() {
                if (msg.sender == _admin()) {
                    _;
                } else {
                    _fallback();
                }
            }
            /**
             * @dev Contract constructor.
             * It sets the `msg.sender` as the proxy administrator.
             * @param implementationContract address of the initial implementation.
             */
            constructor(address implementationContract)
                public
                UpgradeabilityProxy(implementationContract)
            {
                assert(ADMIN_SLOT == keccak256("org.zeppelinos.proxy.admin"));
                _setAdmin(msg.sender);
            }
            /**
             * @return The address of the proxy admin.
             */
            function admin() external view returns (address) {
                return _admin();
            }
            /**
             * @return The address of the implementation.
             */
            function implementation() external view returns (address) {
                return _implementation();
            }
            /**
             * @dev Changes the admin of the proxy.
             * Only the current admin can call this function.
             * @param newAdmin Address to transfer proxy administration to.
             */
            function changeAdmin(address newAdmin) external ifAdmin {
                require(
                    newAdmin != address(0),
                    "Cannot change the admin of a proxy to the zero address"
                );
                emit AdminChanged(_admin(), newAdmin);
                _setAdmin(newAdmin);
            }
            /**
             * @dev Upgrade the backing implementation of the proxy.
             * Only the admin can call this function.
             * @param newImplementation Address of the new implementation.
             */
            function upgradeTo(address newImplementation) external ifAdmin {
                _upgradeTo(newImplementation);
            }
            /**
             * @dev Upgrade the backing implementation of the proxy and call a function
             * on the new implementation.
             * This is useful to initialize the proxied contract.
             * @param newImplementation Address of the new implementation.
             * @param data Data to send as msg.data in the low level call.
             * It should include the signature and the parameters of the function to be
             * called, as described in
             * https://solidity.readthedocs.io/en/develop/abi-spec.html#function-selector-and-argument-encoding.
             */
            function upgradeToAndCall(address newImplementation, bytes calldata data)
                external
                payable
                ifAdmin
            {
                _upgradeTo(newImplementation);
                // prettier-ignore
                // solhint-disable-next-line avoid-low-level-calls
                (bool success,) = address(this).call{value: msg.value}(data);
                // solhint-disable-next-line reason-string
                require(success);
            }
            /**
             * @return adm The admin slot.
             */
            function _admin() internal view returns (address adm) {
                bytes32 slot = ADMIN_SLOT;
                assembly {
                    adm := sload(slot)
                }
            }
            /**
             * @dev Sets the address of the proxy admin.
             * @param newAdmin Address of the new proxy admin.
             */
            function _setAdmin(address newAdmin) internal {
                bytes32 slot = ADMIN_SLOT;
                assembly {
                    sstore(slot, newAdmin)
                }
            }
            /**
             * @dev Only fall back when the sender is not the admin.
             */
            function _willFallback() internal override {
                require(
                    msg.sender != _admin(),
                    "Cannot call fallback function from the proxy admin"
                );
                super._willFallback();
            }
        }
        /**
         * SPDX-License-Identifier: MIT
         *
         * Copyright (c) 2018 zOS Global Limited.
         *
         * Permission is hereby granted, free of charge, to any person obtaining a copy
         * of this software and associated documentation files (the "Software"), to deal
         * in the Software without restriction, including without limitation the rights
         * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
         * copies of the Software, and to permit persons to whom the Software is
         * furnished to do so, subject to the following conditions:
         *
         * The above copyright notice and this permission notice shall be included in
         * copies or substantial portions of the Software.
         *
         * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
         * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
         * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
         * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
         * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
         * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
         * SOFTWARE.
         */
        pragma solidity 0.6.12;
        import { Proxy } from "./Proxy.sol";
        import { Address } from "@openzeppelin/contracts/utils/Address.sol";
        /**
         * @notice This contract implements a proxy that allows to change the
         * implementation address to which it will delegate.
         * Such a change is called an implementation upgrade.
         * @dev Forked from https://github.com/zeppelinos/zos-lib/blob/8a16ef3ad17ec7430e3a9d2b5e3f39b8204f8c8d/contracts/upgradeability/UpgradeabilityProxy.sol
         * Modifications:
         * 1. Reformat, conform to Solidity 0.6 syntax, and add error messages (5/13/20)
         * 2. Use Address utility library from the latest OpenZeppelin (5/13/20)
         */
        contract UpgradeabilityProxy is Proxy {
            /**
             * @dev Emitted when the implementation is upgraded.
             * @param implementation Address of the new implementation.
             */
            event Upgraded(address implementation);
            /**
             * @dev Storage slot with the address of the current implementation.
             * This is the keccak-256 hash of "org.zeppelinos.proxy.implementation", and is
             * validated in the constructor.
             */
            bytes32
                private constant IMPLEMENTATION_SLOT = 0x7050c9e0f4ca769c69bd3a8ef740bc37934f8e2c036e5a723fd8ee048ed3f8c3;
            /**
             * @dev Contract constructor.
             * @param implementationContract Address of the initial implementation.
             */
            constructor(address implementationContract) public {
                assert(
                    IMPLEMENTATION_SLOT ==
                        keccak256("org.zeppelinos.proxy.implementation")
                );
                _setImplementation(implementationContract);
            }
            /**
             * @dev Returns the current implementation.
             * @return impl Address of the current implementation
             */
            function _implementation() internal override view returns (address impl) {
                bytes32 slot = IMPLEMENTATION_SLOT;
                assembly {
                    impl := sload(slot)
                }
            }
            /**
             * @dev Upgrades the proxy to a new implementation.
             * @param newImplementation Address of the new implementation.
             */
            function _upgradeTo(address newImplementation) internal {
                _setImplementation(newImplementation);
                emit Upgraded(newImplementation);
            }
            /**
             * @dev Sets the implementation address of the proxy.
             * @param newImplementation Address of the new implementation.
             */
            function _setImplementation(address newImplementation) private {
                require(
                    Address.isContract(newImplementation),
                    "Cannot set a proxy implementation to a non-contract address"
                );
                bytes32 slot = IMPLEMENTATION_SLOT;
                assembly {
                    sstore(slot, newImplementation)
                }
            }
        }
        /**
         * SPDX-License-Identifier: MIT
         *
         * Copyright (c) 2018 zOS Global Limited.
         *
         * Permission is hereby granted, free of charge, to any person obtaining a copy
         * of this software and associated documentation files (the "Software"), to deal
         * in the Software without restriction, including without limitation the rights
         * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
         * copies of the Software, and to permit persons to whom the Software is
         * furnished to do so, subject to the following conditions:
         *
         * The above copyright notice and this permission notice shall be included in
         * copies or substantial portions of the Software.
         *
         * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
         * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
         * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
         * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
         * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
         * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
         * SOFTWARE.
         */
        pragma solidity 0.6.12;
        /**
         * @notice Implements delegation of calls to other contracts, with proper
         * forwarding of return values and bubbling of failures.
         * It defines a fallback function that delegates all calls to the address
         * returned by the abstract _implementation() internal function.
         * @dev Forked from https://github.com/zeppelinos/zos-lib/blob/8a16ef3ad17ec7430e3a9d2b5e3f39b8204f8c8d/contracts/upgradeability/Proxy.sol
         * Modifications:
         * 1. Reformat and conform to Solidity 0.6 syntax (5/13/20)
         */
        abstract contract Proxy {
            /**
             * @dev Fallback function.
             * Implemented entirely in `_fallback`.
             */
            fallback() external payable {
                _fallback();
            }
            /**
             * @return The Address of the implementation.
             */
            function _implementation() internal virtual view returns (address);
            /**
             * @dev Delegates execution to an implementation contract.
             * This is a low level function that doesn't return to its internal call site.
             * It will return to the external caller whatever the implementation returns.
             * @param implementation Address to delegate.
             */
            function _delegate(address implementation) internal {
                assembly {
                    // Copy msg.data. We take full control of memory in this inline assembly
                    // block because it will not return to Solidity code. We overwrite the
                    // Solidity scratch pad at memory position 0.
                    calldatacopy(0, 0, calldatasize())
                    // Call the implementation.
                    // out and outsize are 0 because we don't know the size yet.
                    let result := delegatecall(
                        gas(),
                        implementation,
                        0,
                        calldatasize(),
                        0,
                        0
                    )
                    // Copy the returned data.
                    returndatacopy(0, 0, returndatasize())
                    switch result
                        // delegatecall returns 0 on error.
                        case 0 {
                            revert(0, returndatasize())
                        }
                        default {
                            return(0, returndatasize())
                        }
                }
            }
            /**
             * @dev Function that is run as the first thing in the fallback function.
             * Can be redefined in derived contracts to add functionality.
             * Redefinitions must call super._willFallback().
             */
            function _willFallback() internal virtual {}
            /**
             * @dev fallback implementation.
             * Extracted to enable manual triggering.
             */
            function _fallback() internal {
                _willFallback();
                _delegate(_implementation());
            }
        }
        // SPDX-License-Identifier: MIT
        pragma solidity ^0.6.2;
        /**
         * @dev Collection of functions related to the address type
         */
        library Address {
            /**
             * @dev Returns true if `account` is a contract.
             *
             * [IMPORTANT]
             * ====
             * It is unsafe to assume that an address for which this function returns
             * false is an externally-owned account (EOA) and not a contract.
             *
             * Among others, `isContract` will return false for the following
             * types of addresses:
             *
             *  - an externally-owned account
             *  - a contract in construction
             *  - an address where a contract will be created
             *  - an address where a contract lived, but was destroyed
             * ====
             */
            function isContract(address account) internal view returns (bool) {
                // According to EIP-1052, 0x0 is the value returned for not-yet created accounts
                // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
                // for accounts without code, i.e. `keccak256('')`
                bytes32 codehash;
                bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
                // solhint-disable-next-line no-inline-assembly
                assembly { codehash := extcodehash(account) }
                return (codehash != accountHash && codehash != 0x0);
            }
            /**
             * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
             * `recipient`, forwarding all available gas and reverting on errors.
             *
             * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
             * of certain opcodes, possibly making contracts go over the 2300 gas limit
             * imposed by `transfer`, making them unable to receive funds via
             * `transfer`. {sendValue} removes this limitation.
             *
             * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
             *
             * IMPORTANT: because control is transferred to `recipient`, care must be
             * taken to not create reentrancy vulnerabilities. Consider using
             * {ReentrancyGuard} or the
             * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
             */
            function sendValue(address payable recipient, uint256 amount) internal {
                require(address(this).balance >= amount, "Address: insufficient balance");
                // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
                (bool success, ) = recipient.call{ value: amount }("");
                require(success, "Address: unable to send value, recipient may have reverted");
            }
            /**
             * @dev Performs a Solidity function call using a low level `call`. A
             * plain`call` is an unsafe replacement for a function call: use this
             * function instead.
             *
             * If `target` reverts with a revert reason, it is bubbled up by this
             * function (like regular Solidity function calls).
             *
             * Returns the raw returned data. To convert to the expected return value,
             * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
             *
             * Requirements:
             *
             * - `target` must be a contract.
             * - calling `target` with `data` must not revert.
             *
             * _Available since v3.1._
             */
            function functionCall(address target, bytes memory data) internal returns (bytes memory) {
              return functionCall(target, data, "Address: low-level call failed");
            }
            /**
             * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
             * `errorMessage` as a fallback revert reason when `target` reverts.
             *
             * _Available since v3.1._
             */
            function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
                return _functionCallWithValue(target, data, 0, errorMessage);
            }
            /**
             * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
             * but also transferring `value` wei to `target`.
             *
             * Requirements:
             *
             * - the calling contract must have an ETH balance of at least `value`.
             * - the called Solidity function must be `payable`.
             *
             * _Available since v3.1._
             */
            function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
                return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
            }
            /**
             * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
             * with `errorMessage` as a fallback revert reason when `target` reverts.
             *
             * _Available since v3.1._
             */
            function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
                require(address(this).balance >= value, "Address: insufficient balance for call");
                return _functionCallWithValue(target, data, value, errorMessage);
            }
            function _functionCallWithValue(address target, bytes memory data, uint256 weiValue, string memory errorMessage) private returns (bytes memory) {
                require(isContract(target), "Address: call to non-contract");
                // solhint-disable-next-line avoid-low-level-calls
                (bool success, bytes memory returndata) = target.call{ value: weiValue }(data);
                if (success) {
                    return returndata;
                } else {
                    // Look for revert reason and bubble it up if present
                    if (returndata.length > 0) {
                        // The easiest way to bubble the revert reason is using memory via assembly
                        // solhint-disable-next-line no-inline-assembly
                        assembly {
                            let returndata_size := mload(returndata)
                            revert(add(32, returndata), returndata_size)
                        }
                    } else {
                        revert(errorMessage);
                    }
                }
            }
        }
        

        File 3 of 4: DefaultCollateral
        // SPDX-License-Identifier: MIT
        pragma solidity 0.8.25;
        import {IDefaultCollateral} from "src/interfaces/defaultCollateral/IDefaultCollateral.sol";
        import {ICollateral} from "src/interfaces/ICollateral.sol";
        import {Permit2Lib} from "src/contracts/libraries/Permit2Lib.sol";
        import {ReentrancyGuardUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
        import {ERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
        import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
        import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
        import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
        contract DefaultCollateral is ERC20Upgradeable, ReentrancyGuardUpgradeable, IDefaultCollateral {
            using SafeERC20 for IERC20;
            using Permit2Lib for IERC20;
            uint8 private DECIMALS;
            /**
             * @inheritdoc ICollateral
             */
            address public asset;
            /**
             * @inheritdoc ICollateral
             */
            uint256 public totalRepaidDebt;
            /**
             * @inheritdoc ICollateral
             */
            mapping(address issuer => uint256 amount) public issuerRepaidDebt;
            /**
             * @inheritdoc ICollateral
             */
            mapping(address recipient => uint256 amount) public recipientRepaidDebt;
            /**
             * @inheritdoc ICollateral
             */
            mapping(address issuer => mapping(address recipient => uint256 amount)) public repaidDebt;
            /**
             * @inheritdoc ICollateral
             */
            uint256 public totalDebt;
            /**
             * @inheritdoc ICollateral
             */
            mapping(address issuer => uint256 amount) public issuerDebt;
            /**
             * @inheritdoc ICollateral
             */
            mapping(address recipient => uint256 amount) public recipientDebt;
            /**
             * @inheritdoc ICollateral
             */
            mapping(address issuer => mapping(address recipient => uint256 amount)) public debt;
            /**
             * @inheritdoc IDefaultCollateral
             */
            uint256 public limit;
            /**
             * @inheritdoc IDefaultCollateral
             */
            address public limitIncreaser;
            modifier onlyLimitIncreaser() {
                if (msg.sender != limitIncreaser) {
                    revert NotLimitIncreaser();
                }
                _;
            }
            constructor() {
                _disableInitializers();
            }
            function initialize(address asset_, uint256 initialLimit, address limitIncreaser_) external initializer {
                __ERC20_init(
                    string.concat("DefaultCollateral_", IERC20Metadata(asset_).name()),
                    string.concat("DC_", IERC20Metadata(asset_).symbol())
                );
                __ReentrancyGuard_init();
                asset = asset_;
                limit = initialLimit;
                limitIncreaser = limitIncreaser_;
                DECIMALS = IERC20Metadata(asset).decimals();
            }
            /**
             * @inheritdoc ERC20Upgradeable
             */
            function decimals() public view override returns (uint8) {
                return DECIMALS;
            }
            /**
             * @inheritdoc IDefaultCollateral
             */
            function deposit(address recipient, uint256 amount) public nonReentrant returns (uint256) {
                uint256 balanceBefore = IERC20(asset).balanceOf(address(this));
                IERC20(asset).transferFrom2(msg.sender, address(this), amount);
                amount = IERC20(asset).balanceOf(address(this)) - balanceBefore;
                if (amount == 0) {
                    revert InsufficientDeposit();
                }
                if (totalSupply() + amount > limit) {
                    revert ExceedsLimit();
                }
                _mint(recipient, amount);
                emit Deposit(msg.sender, recipient, amount);
                return amount;
            }
            /**
             * @inheritdoc IDefaultCollateral
             */
            function deposit(
                address recipient,
                uint256 amount,
                uint256 deadline,
                uint8 v,
                bytes32 r,
                bytes32 s
            ) external returns (uint256) {
                IERC20(asset).tryPermit2(msg.sender, address(this), amount, deadline, v, r, s);
                return deposit(recipient, amount);
            }
            /**
             * @inheritdoc IDefaultCollateral
             */
            function withdraw(address recipient, uint256 amount) external {
                if (amount == 0) {
                    revert InsufficientWithdraw();
                }
                _burn(msg.sender, amount);
                IERC20(asset).safeTransfer(recipient, amount);
                emit Withdraw(msg.sender, recipient, amount);
            }
            /**
             * @inheritdoc ICollateral
             */
            function issueDebt(address recipient, uint256 amount) external {
                if (amount == 0) {
                    revert InsufficientIssueDebt();
                }
                _burn(msg.sender, amount);
                emit IssueDebt(msg.sender, recipient, amount);
                totalRepaidDebt += amount;
                issuerRepaidDebt[msg.sender] += amount;
                recipientRepaidDebt[recipient] += amount;
                repaidDebt[msg.sender][recipient] += amount;
                IERC20(asset).safeTransfer(recipient, amount);
                emit RepayDebt(msg.sender, recipient, amount);
            }
            /**
             * @inheritdoc IDefaultCollateral
             */
            function increaseLimit(uint256 amount) external onlyLimitIncreaser {
                limit += amount;
                emit IncreaseLimit(amount);
            }
            /**
             * @inheritdoc IDefaultCollateral
             */
            function setLimitIncreaser(address limitIncreaser_) external onlyLimitIncreaser {
                limitIncreaser = limitIncreaser_;
                emit SetLimitIncreaser(limitIncreaser_);
            }
        }
        // SPDX-License-Identifier: MIT
        pragma solidity 0.8.25;
        import {ICollateral} from "src/interfaces/ICollateral.sol";
        interface IDefaultCollateral is ICollateral {
            error NotLimitIncreaser();
            error InsufficientDeposit();
            error ExceedsLimit();
            error InsufficientWithdraw();
            error InsufficientIssueDebt();
            /**
             * @notice Emmited when deposit happens.
             * @param depositor address of the depositor
             * @param recipient address of the collateral's recipient
             * @param amount amount of the collateral minted
             */
            event Deposit(address indexed depositor, address indexed recipient, uint256 amount);
            /**
             * @notice Emmited when withdrawal happens.
             * @param withdrawer address of the withdrawer
             * @param recipient address of the underlying asset's recipient
             * @param amount amount of the collateral burned
             */
            event Withdraw(address indexed withdrawer, address indexed recipient, uint256 amount);
            /**
             * @notice Emmited when limit is increased.
             * @param amount amount to increase the limit by
             */
            event IncreaseLimit(uint256 amount);
            /**
             * @notice Emmited when new limit increaser is set.
             * @param limitIncreaser address of the new limit increaser
             */
            event SetLimitIncreaser(address indexed limitIncreaser);
            /**
             * @notice Get a maximum possible collateral total supply.
             * @return maximum collateral total supply
             */
            function limit() external view returns (uint256);
            /**
             * @notice Get an address of the limit increaser.
             * @return address of the limit increaser
             */
            function limitIncreaser() external view returns (address);
            /**
             * @notice Deposit a given amount of the underlying asset, and mint the collateral to a particular recipient.
             * @param recipient address of the collateral's recipient
             * @param amount amount of the underlying asset
             * @return amount of the collateral minted
             */
            function deposit(address recipient, uint256 amount) external returns (uint256);
            /**
             * @notice Deposit a given amount of the underlying asset using a permit functionality, and mint the collateral to a particular recipient.
             * @param recipient address of the collateral's recipient
             * @param amount amount of the underlying asset
             * @param deadline timestamp of the signature's deadline
             * @param v v component of the signature
             * @param r r component of the signature
             * @param s s component of the signature
             * @return amount of the collateral minted
             */
            function deposit(
                address recipient,
                uint256 amount,
                uint256 deadline,
                uint8 v,
                bytes32 r,
                bytes32 s
            ) external returns (uint256);
            /**
             * @notice Withdraw a given amount of the underlying asset, and transfer it to a particular recipient.
             * @param recipient address of the underlying asset's recipient
             * @param amount amount of the underlying asset
             */
            function withdraw(address recipient, uint256 amount) external;
            /**
             * @notice Increase a limit of maximum collateral total supply.
             * @param amount amount to increase the limit by
             * @dev Called only by limitIncreaser.
             */
            function increaseLimit(uint256 amount) external;
            /**
             * @notice Set a new limit increaser.
             * @param limitIncreaser address of the new limit increaser
             * @dev Called only by limitIncreaser.
             */
            function setLimitIncreaser(address limitIncreaser) external;
        }
        // SPDX-License-Identifier: MIT
        pragma solidity 0.8.25;
        import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
        interface ICollateral is IERC20 {
            /**
             * @notice Emitted when debt is issued.
             * @param issuer address of the debt's issuer
             * @param recipient address that should receive the underlying asset
             * @param debtIssued amount of the debt issued
             */
            event IssueDebt(address indexed issuer, address indexed recipient, uint256 debtIssued);
            /**
             * @notice Emitted when debt is repaid.
             * @param issuer address of the debt's issuer
             * @param recipient address that received the underlying asset
             * @param debtRepaid amount of the debt repaid
             */
            event RepayDebt(address indexed issuer, address indexed recipient, uint256 debtRepaid);
            /**
             * @notice Get the collateral's underlying asset.
             * @return asset address of the underlying asset
             */
            function asset() external view returns (address);
            /**
             * @notice Get a total amount of repaid debt.
             * @return total repaid debt
             */
            function totalRepaidDebt() external view returns (uint256);
            /**
             * @notice Get an amount of repaid debt created by a particular issuer.
             * @param issuer address of the debt's issuer
             * @return particular issuer's repaid debt
             */
            function issuerRepaidDebt(address issuer) external view returns (uint256);
            /**
             * @notice Get an amount of repaid debt to a particular recipient.
             * @param recipient address that received the underlying asset
             * @return particular recipient's repaid debt
             */
            function recipientRepaidDebt(address recipient) external view returns (uint256);
            /**
             * @notice Get an amount of repaid debt for a particular issuer-recipient pair.
             * @param issuer address of the debt's issuer
             * @param recipient address that received the underlying asset
             * @return particular pair's repaid debt
             */
            function repaidDebt(address issuer, address recipient) external view returns (uint256);
            /**
             * @notice Get a total amount of debt.
             * @return total debt
             */
            function totalDebt() external view returns (uint256);
            /**
             * @notice Get a current debt created by a particular issuer.
             * @param issuer address of the debt's issuer
             * @return particular issuer's debt
             */
            function issuerDebt(address issuer) external view returns (uint256);
            /**
             * @notice Get a current debt to a particular recipient.
             * @param recipient address that should receive the underlying asset
             * @return particular recipient's debt
             */
            function recipientDebt(address recipient) external view returns (uint256);
            /**
             * @notice Get a current debt for a particular issuer-recipient pair.
             * @param issuer address of the debt's issuer
             * @param recipient address that should receive the underlying asset
             * @return particular pair's debt
             */
            function debt(address issuer, address recipient) external view returns (uint256);
            /**
             * @notice Burn a given amount of the collateral, and increase a debt of the underlying asset for the caller.
             * @param recipient address that should receive the underlying asset
             * @param amount amount of the collateral
             */
            function issueDebt(address recipient, uint256 amount) external;
        }
        // SPDX-License-Identifier: MIT
        pragma solidity ^0.8.17;
        import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
        import {IERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol";
        import {IDAIPermit} from "permit2/src/interfaces/IDAIPermit.sol";
        import {IAllowanceTransfer} from "permit2/src/interfaces/IAllowanceTransfer.sol";
        import {SafeCast160} from "permit2/src/libraries/SafeCast160.sol";
        /// @title Permit2Lib
        /// @notice Enables efficient transfers and EIP-2612/DAI
        /// permits for any token by falling back to Permit2.
        /// @author Modified from Uniswap (https://github.com/Uniswap/permit2/blob/main/src/libraries/Permit2Lib.sol)
        library Permit2Lib {
            using SafeCast160 for uint256;
            /*//////////////////////////////////////////////////////////////
                                        CONSTANTS
            //////////////////////////////////////////////////////////////*/
            /// @dev The unique EIP-712 domain domain separator for the DAI token contract.
            bytes32 internal constant DAI_DOMAIN_SEPARATOR = 0xdbb8cf42e1ecb028be3f3dbc922e1d878b963f411dc388ced501601c60f7c6f7;
            /// @dev The address for the WETH9 contract on Ethereum mainnet, encoded as a bytes32.
            bytes32 internal constant WETH9_ADDRESS = 0x000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2;
            /// @dev The address of the Permit2 contract the library will use.
            IAllowanceTransfer internal constant PERMIT2 =
                IAllowanceTransfer(address(0x000000000022D473030F116dDEE9F6B43aC78BA3));
            /// @notice Transfer a given amount of tokens from one user to another.
            /// @param token The token to transfer.
            /// @param from The user to transfer from.
            /// @param to The user to transfer to.
            /// @param amount The amount to transfer.
            function transferFrom2(IERC20 token, address from, address to, uint256 amount) internal {
                // Generate calldata for a standard transferFrom call.
                bytes memory inputData = abi.encodeCall(IERC20.transferFrom, (from, to, amount));
                bool success; // Call the token contract as normal, capturing whether it succeeded.
                assembly {
                    success :=
                        and(
                            // Set success to whether the call reverted, if not we check it either
                            // returned exactly 1 (can't just be non-zero data), or had no return data.
                            or(eq(mload(0), 1), iszero(returndatasize())),
                            // Counterintuitively, this call() must be positioned after the or() in the
                            // surrounding and() because and() evaluates its arguments from right to left.
                            // We use 0 and 32 to copy up to 32 bytes of return data into the first slot of scratch space.
                            call(gas(), token, 0, add(inputData, 32), mload(inputData), 0, 32)
                        )
                }
                // We'll fall back to using Permit2 if calling transferFrom on the token directly reverted.
                if (!success) PERMIT2.transferFrom(from, to, amount.toUint160(), address(token));
            }
            /*//////////////////////////////////////////////////////////////
                                      PERMIT LOGIC
            //////////////////////////////////////////////////////////////*/
            /// @notice Permit a user to spend a given amount of
            /// another user's tokens via native EIP-2612 permit if possible, falling
            /// back to Permit2 if native permit fails or is not implemented on the token.
            /// @param token The token to permit spending.
            /// @param owner The user to permit spending from.
            /// @param spender The user to permit spending to.
            /// @param amount The amount to permit spending.
            /// @param deadline  The timestamp after which the signature is no longer valid.
            /// @param v Must produce valid secp256k1 signature from the owner along with r and s.
            /// @param r Must produce valid secp256k1 signature from the owner along with v and s.
            /// @param s Must produce valid secp256k1 signature from the owner along with r and v.
            function tryPermit2(
                IERC20 token,
                address owner,
                address spender,
                uint256 amount,
                uint256 deadline,
                uint8 v,
                bytes32 r,
                bytes32 s
            ) internal {
                // Generate calldata for a call to DOMAIN_SEPARATOR on the token.
                bytes memory inputData = abi.encodeWithSelector(IERC20Permit.DOMAIN_SEPARATOR.selector);
                bool success; // Call the token contract as normal, capturing whether it succeeded.
                bytes32 domainSeparator; // If the call succeeded, we'll capture the return value here.
                assembly {
                    // If the token is WETH9, we know it doesn't have a DOMAIN_SEPARATOR, and we'll skip this step.
                    // We make sure to mask the token address as its higher order bits aren't guaranteed to be clean.
                    if iszero(eq(and(token, 0xffffffffffffffffffffffffffffffffffffffff), WETH9_ADDRESS)) {
                        success :=
                            and(
                                // Should resolve false if its not 32 bytes or its first word is 0.
                                and(iszero(iszero(mload(0))), eq(returndatasize(), 32)),
                                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                                // Counterintuitively, this call must be positioned second to the and() call in the
                                // surrounding and() call or else returndatasize() will be zero during the computation.
                                // We send a maximum of 5000 gas to prevent tokens with fallbacks from using a ton of gas.
                                // which should be plenty to allow tokens to fetch their DOMAIN_SEPARATOR from storage, etc.
                                staticcall(5000, token, add(inputData, 32), mload(inputData), 0, 32)
                            )
                        domainSeparator := mload(0) // Copy the return value into the domainSeparator variable.
                    }
                }
                // If the call to DOMAIN_SEPARATOR succeeded, try using permit on the token.
                if (success) {
                    // We'll use DAI's special permit if it's DOMAIN_SEPARATOR matches,
                    // otherwise we'll just encode a call to the standard permit function.
                    inputData = domainSeparator == DAI_DOMAIN_SEPARATOR
                        ? abi.encodeCall(
                            IDAIPermit.permit, (owner, spender, IERC20Permit(address(token)).nonces(owner), deadline, true, v, r, s)
                        )
                        : abi.encodeCall(IERC20Permit.permit, (owner, spender, amount, deadline, v, r, s));
                    assembly {
                        success := call(gas(), token, 0, add(inputData, 32), mload(inputData), 0, 0)
                    }
                }
                if (!success) {
                    // If the initial DOMAIN_SEPARATOR call on the token failed or a
                    // subsequent call to permit failed, fall back to using Permit2.
                    simplePermit2(token, owner, spender, amount, deadline, v, r, s);
                }
            }
            /// @notice Simple unlimited permit on the Permit2 contract.
            /// @param token The token to permit spending.
            /// @param owner The user to permit spending from.
            /// @param spender The user to permit spending to.
            /// @param amount The amount to permit spending.
            /// @param deadline  The timestamp after which the signature is no longer valid.
            /// @param v Must produce valid secp256k1 signature from the owner along with r and s.
            /// @param r Must produce valid secp256k1 signature from the owner along with v and s.
            /// @param s Must produce valid secp256k1 signature from the owner along with r and v.
            function simplePermit2(
                IERC20 token,
                address owner,
                address spender,
                uint256 amount,
                uint256 deadline,
                uint8 v,
                bytes32 r,
                bytes32 s
            ) internal {
                (,, uint48 nonce) = PERMIT2.allowance(owner, address(token), spender);
                try PERMIT2.permit(
                    owner,
                    IAllowanceTransfer.PermitSingle({
                        details: IAllowanceTransfer.PermitDetails({
                            token: address(token),
                            amount: amount.toUint160(),
                            // Use an unlimited expiration because it most
                            // closely mimics how a standard approval works.
                            expiration: type(uint48).max,
                            nonce: nonce
                        }),
                        spender: spender,
                        sigDeadline: deadline
                    }),
                    bytes.concat(r, s, bytes1(v))
                ) {} catch {}
            }
        }
        // SPDX-License-Identifier: MIT
        // OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol)
        pragma solidity ^0.8.20;
        import {Initializable} from "../proxy/utils/Initializable.sol";
        /**
         * @dev Contract module that helps prevent reentrant calls to a function.
         *
         * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
         * available, which can be applied to functions to make sure there are no nested
         * (reentrant) calls to them.
         *
         * Note that because there is a single `nonReentrant` guard, functions marked as
         * `nonReentrant` may not call one another. This can be worked around by making
         * those functions `private`, and then adding `external` `nonReentrant` entry
         * points to them.
         *
         * TIP: If you would like to learn more about reentrancy and alternative ways
         * to protect against it, check out our blog post
         * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
         */
        abstract contract ReentrancyGuardUpgradeable is Initializable {
            // 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;
            /// @custom:storage-location erc7201:openzeppelin.storage.ReentrancyGuard
            struct ReentrancyGuardStorage {
                uint256 _status;
            }
            // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ReentrancyGuard")) - 1)) & ~bytes32(uint256(0xff))
            bytes32 private constant ReentrancyGuardStorageLocation = 0x9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00;
            function _getReentrancyGuardStorage() private pure returns (ReentrancyGuardStorage storage $) {
                assembly {
                    $.slot := ReentrancyGuardStorageLocation
                }
            }
            /**
             * @dev Unauthorized reentrant call.
             */
            error ReentrancyGuardReentrantCall();
            function __ReentrancyGuard_init() internal onlyInitializing {
                __ReentrancyGuard_init_unchained();
            }
            function __ReentrancyGuard_init_unchained() internal onlyInitializing {
                ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
                $._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 {
                ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
                // On the first call to nonReentrant, _status will be NOT_ENTERED
                if ($._status == ENTERED) {
                    revert ReentrancyGuardReentrantCall();
                }
                // Any calls to nonReentrant after this point will fail
                $._status = ENTERED;
            }
            function _nonReentrantAfter() private {
                ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
                // By storing the original value once again, a refund is triggered (see
                // https://eips.ethereum.org/EIPS/eip-2200)
                $._status = NOT_ENTERED;
            }
            /**
             * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
             * `nonReentrant` function in the call stack.
             */
            function _reentrancyGuardEntered() internal view returns (bool) {
                ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
                return $._status == ENTERED;
            }
        }
        // SPDX-License-Identifier: MIT
        // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/ERC20.sol)
        pragma solidity ^0.8.20;
        import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
        import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
        import {ContextUpgradeable} from "../../utils/ContextUpgradeable.sol";
        import {IERC20Errors} from "@openzeppelin/contracts/interfaces/draft-IERC6093.sol";
        import {Initializable} from "../../proxy/utils/Initializable.sol";
        /**
         * @dev Implementation of the {IERC20} interface.
         *
         * This implementation is agnostic to the way tokens are created. This means
         * that a supply mechanism has to be added in a derived contract using {_mint}.
         *
         * TIP: For a detailed writeup see our guide
         * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
         * to implement supply mechanisms].
         *
         * The default value of {decimals} is 18. To change this, you should override
         * this function so it returns a different value.
         *
         * We have followed general OpenZeppelin Contracts guidelines: functions revert
         * instead returning `false` on failure. This behavior is nonetheless
         * conventional and does not conflict with the expectations of ERC20
         * applications.
         *
         * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
         * This allows applications to reconstruct the allowance for all accounts just
         * by listening to said events. Other implementations of the EIP may not emit
         * these events, as it isn't required by the specification.
         */
        abstract contract ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20, IERC20Metadata, IERC20Errors {
            /// @custom:storage-location erc7201:openzeppelin.storage.ERC20
            struct ERC20Storage {
                mapping(address account => uint256) _balances;
                mapping(address account => mapping(address spender => uint256)) _allowances;
                uint256 _totalSupply;
                string _name;
                string _symbol;
            }
            // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ERC20")) - 1)) & ~bytes32(uint256(0xff))
            bytes32 private constant ERC20StorageLocation = 0x52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace00;
            function _getERC20Storage() private pure returns (ERC20Storage storage $) {
                assembly {
                    $.slot := ERC20StorageLocation
                }
            }
            /**
             * @dev Sets the values for {name} and {symbol}.
             *
             * All two of these values are immutable: they can only be set once during
             * construction.
             */
            function __ERC20_init(string memory name_, string memory symbol_) internal onlyInitializing {
                __ERC20_init_unchained(name_, symbol_);
            }
            function __ERC20_init_unchained(string memory name_, string memory symbol_) internal onlyInitializing {
                ERC20Storage storage $ = _getERC20Storage();
                $._name = name_;
                $._symbol = symbol_;
            }
            /**
             * @dev Returns the name of the token.
             */
            function name() public view virtual returns (string memory) {
                ERC20Storage storage $ = _getERC20Storage();
                return $._name;
            }
            /**
             * @dev Returns the symbol of the token, usually a shorter version of the
             * name.
             */
            function symbol() public view virtual returns (string memory) {
                ERC20Storage storage $ = _getERC20Storage();
                return $._symbol;
            }
            /**
             * @dev Returns the number of decimals used to get its user representation.
             * For example, if `decimals` equals `2`, a balance of `505` tokens should
             * be displayed to a user as `5.05` (`505 / 10 ** 2`).
             *
             * Tokens usually opt for a value of 18, imitating the relationship between
             * Ether and Wei. This is the default value returned by this function, unless
             * it's overridden.
             *
             * NOTE: This information is only used for _display_ purposes: it in
             * no way affects any of the arithmetic of the contract, including
             * {IERC20-balanceOf} and {IERC20-transfer}.
             */
            function decimals() public view virtual returns (uint8) {
                return 18;
            }
            /**
             * @dev See {IERC20-totalSupply}.
             */
            function totalSupply() public view virtual returns (uint256) {
                ERC20Storage storage $ = _getERC20Storage();
                return $._totalSupply;
            }
            /**
             * @dev See {IERC20-balanceOf}.
             */
            function balanceOf(address account) public view virtual returns (uint256) {
                ERC20Storage storage $ = _getERC20Storage();
                return $._balances[account];
            }
            /**
             * @dev See {IERC20-transfer}.
             *
             * Requirements:
             *
             * - `to` cannot be the zero address.
             * - the caller must have a balance of at least `value`.
             */
            function transfer(address to, uint256 value) public virtual returns (bool) {
                address owner = _msgSender();
                _transfer(owner, to, value);
                return true;
            }
            /**
             * @dev See {IERC20-allowance}.
             */
            function allowance(address owner, address spender) public view virtual returns (uint256) {
                ERC20Storage storage $ = _getERC20Storage();
                return $._allowances[owner][spender];
            }
            /**
             * @dev See {IERC20-approve}.
             *
             * NOTE: If `value` is the maximum `uint256`, the allowance is not updated on
             * `transferFrom`. This is semantically equivalent to an infinite approval.
             *
             * Requirements:
             *
             * - `spender` cannot be the zero address.
             */
            function approve(address spender, uint256 value) public virtual returns (bool) {
                address owner = _msgSender();
                _approve(owner, spender, value);
                return true;
            }
            /**
             * @dev See {IERC20-transferFrom}.
             *
             * Emits an {Approval} event indicating the updated allowance. This is not
             * required by the EIP. See the note at the beginning of {ERC20}.
             *
             * NOTE: Does not update the allowance if the current allowance
             * is the maximum `uint256`.
             *
             * Requirements:
             *
             * - `from` and `to` cannot be the zero address.
             * - `from` must have a balance of at least `value`.
             * - the caller must have allowance for ``from``'s tokens of at least
             * `value`.
             */
            function transferFrom(address from, address to, uint256 value) public virtual returns (bool) {
                address spender = _msgSender();
                _spendAllowance(from, spender, value);
                _transfer(from, to, value);
                return true;
            }
            /**
             * @dev Moves a `value` amount of tokens from `from` to `to`.
             *
             * This internal function is equivalent to {transfer}, and can be used to
             * e.g. implement automatic token fees, slashing mechanisms, etc.
             *
             * Emits a {Transfer} event.
             *
             * NOTE: This function is not virtual, {_update} should be overridden instead.
             */
            function _transfer(address from, address to, uint256 value) internal {
                if (from == address(0)) {
                    revert ERC20InvalidSender(address(0));
                }
                if (to == address(0)) {
                    revert ERC20InvalidReceiver(address(0));
                }
                _update(from, to, value);
            }
            /**
             * @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from`
             * (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding
             * this function.
             *
             * Emits a {Transfer} event.
             */
            function _update(address from, address to, uint256 value) internal virtual {
                ERC20Storage storage $ = _getERC20Storage();
                if (from == address(0)) {
                    // Overflow check required: The rest of the code assumes that totalSupply never overflows
                    $._totalSupply += value;
                } else {
                    uint256 fromBalance = $._balances[from];
                    if (fromBalance < value) {
                        revert ERC20InsufficientBalance(from, fromBalance, value);
                    }
                    unchecked {
                        // Overflow not possible: value <= fromBalance <= totalSupply.
                        $._balances[from] = fromBalance - value;
                    }
                }
                if (to == address(0)) {
                    unchecked {
                        // Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply.
                        $._totalSupply -= value;
                    }
                } else {
                    unchecked {
                        // Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.
                        $._balances[to] += value;
                    }
                }
                emit Transfer(from, to, value);
            }
            /**
             * @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0).
             * Relies on the `_update` mechanism
             *
             * Emits a {Transfer} event with `from` set to the zero address.
             *
             * NOTE: This function is not virtual, {_update} should be overridden instead.
             */
            function _mint(address account, uint256 value) internal {
                if (account == address(0)) {
                    revert ERC20InvalidReceiver(address(0));
                }
                _update(address(0), account, value);
            }
            /**
             * @dev Destroys a `value` amount of tokens from `account`, lowering the total supply.
             * Relies on the `_update` mechanism.
             *
             * Emits a {Transfer} event with `to` set to the zero address.
             *
             * NOTE: This function is not virtual, {_update} should be overridden instead
             */
            function _burn(address account, uint256 value) internal {
                if (account == address(0)) {
                    revert ERC20InvalidSender(address(0));
                }
                _update(account, address(0), value);
            }
            /**
             * @dev Sets `value` as the allowance of `spender` over the `owner` s tokens.
             *
             * This internal function is equivalent to `approve`, and can be used to
             * e.g. set automatic allowances for certain subsystems, etc.
             *
             * Emits an {Approval} event.
             *
             * Requirements:
             *
             * - `owner` cannot be the zero address.
             * - `spender` cannot be the zero address.
             *
             * Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
             */
            function _approve(address owner, address spender, uint256 value) internal {
                _approve(owner, spender, value, true);
            }
            /**
             * @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event.
             *
             * By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by
             * `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any
             * `Approval` event during `transferFrom` operations.
             *
             * Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to
             * true using the following override:
             * ```
             * function _approve(address owner, address spender, uint256 value, bool) internal virtual override {
             *     super._approve(owner, spender, value, true);
             * }
             * ```
             *
             * Requirements are the same as {_approve}.
             */
            function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {
                ERC20Storage storage $ = _getERC20Storage();
                if (owner == address(0)) {
                    revert ERC20InvalidApprover(address(0));
                }
                if (spender == address(0)) {
                    revert ERC20InvalidSpender(address(0));
                }
                $._allowances[owner][spender] = value;
                if (emitEvent) {
                    emit Approval(owner, spender, value);
                }
            }
            /**
             * @dev Updates `owner` s allowance for `spender` based on spent `value`.
             *
             * Does not update the allowance value in case of infinite allowance.
             * Revert if not enough allowance is available.
             *
             * Does not emit an {Approval} event.
             */
            function _spendAllowance(address owner, address spender, uint256 value) internal virtual {
                uint256 currentAllowance = allowance(owner, spender);
                if (currentAllowance != type(uint256).max) {
                    if (currentAllowance < value) {
                        revert ERC20InsufficientAllowance(spender, currentAllowance, value);
                    }
                    unchecked {
                        _approve(owner, spender, currentAllowance - value, false);
                    }
                }
            }
        }
        // SPDX-License-Identifier: MIT
        // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)
        pragma solidity ^0.8.20;
        /**
         * @dev Interface of the ERC20 standard as defined in the EIP.
         */
        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 value of tokens in existence.
             */
            function totalSupply() external view returns (uint256);
            /**
             * @dev Returns the value of tokens owned by `account`.
             */
            function balanceOf(address account) external view returns (uint256);
            /**
             * @dev Moves a `value` amount of 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 value) 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 a `value` amount of tokens 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 value) external returns (bool);
            /**
             * @dev Moves a `value` amount of tokens from `from` to `to` using the
             * allowance mechanism. `value` 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 value) external returns (bool);
        }
        // SPDX-License-Identifier: MIT
        // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Metadata.sol)
        pragma solidity ^0.8.20;
        import {IERC20} from "../IERC20.sol";
        /**
         * @dev Interface for the optional metadata functions from the ERC20 standard.
         */
        interface IERC20Metadata is IERC20 {
            /**
             * @dev Returns the name of the token.
             */
            function name() external view returns (string memory);
            /**
             * @dev Returns the symbol of the token.
             */
            function symbol() external view returns (string memory);
            /**
             * @dev Returns the decimals places of the token.
             */
            function decimals() external view returns (uint8);
        }
        // SPDX-License-Identifier: MIT
        // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)
        pragma solidity ^0.8.20;
        import {IERC20} from "../IERC20.sol";
        import {IERC20Permit} from "../extensions/IERC20Permit.sol";
        import {Address} from "../../../utils/Address.sol";
        /**
         * @title SafeERC20
         * @dev Wrappers around ERC20 operations that throw on failure (when the token
         * contract returns false). Tokens that return no value (and instead revert or
         * throw on failure) are also supported, non-reverting calls are assumed to be
         * successful.
         * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
         * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
         */
        library SafeERC20 {
            using Address for address;
            /**
             * @dev An operation with an ERC20 token failed.
             */
            error SafeERC20FailedOperation(address token);
            /**
             * @dev Indicates a failed `decreaseAllowance` request.
             */
            error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
            /**
             * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
             * non-reverting calls are assumed to be successful.
             */
            function safeTransfer(IERC20 token, address to, uint256 value) internal {
                _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
            }
            /**
             * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
             * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
             */
            function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
                _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
            }
            /**
             * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
             * non-reverting calls are assumed to be successful.
             */
            function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
                uint256 oldAllowance = token.allowance(address(this), spender);
                forceApprove(token, spender, oldAllowance + value);
            }
            /**
             * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
             * value, non-reverting calls are assumed to be successful.
             */
            function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
                unchecked {
                    uint256 currentAllowance = token.allowance(address(this), spender);
                    if (currentAllowance < requestedDecrease) {
                        revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
                    }
                    forceApprove(token, spender, currentAllowance - requestedDecrease);
                }
            }
            /**
             * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
             * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
             * to be set to zero before setting it to a non-zero value, such as USDT.
             */
            function forceApprove(IERC20 token, address spender, uint256 value) internal {
                bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
                if (!_callOptionalReturnBool(token, approvalCall)) {
                    _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
                    _callOptionalReturn(token, approvalCall);
                }
            }
            /**
             * @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);
                if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
                    revert SafeERC20FailedOperation(address(token));
                }
            }
            /**
             * @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).
             *
             * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
             */
            function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
                // 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 cannot use {Address-functionCall} here since this should return false
                // and not revert is the subcall reverts.
                (bool success, bytes memory returndata) = address(token).call(data);
                return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
            }
        }
        // SPDX-License-Identifier: MIT
        // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)
        pragma solidity ^0.8.20;
        /**
         * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
         * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
         *
         * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
         * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
         * need to send a transaction, and thus is not required to hold Ether at all.
         *
         * ==== Security Considerations
         *
         * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
         * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
         * considered as an intention to spend the allowance in any specific way. The second is that because permits have
         * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
         * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
         * generally recommended is:
         *
         * ```solidity
         * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
         *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
         *     doThing(..., value);
         * }
         *
         * function doThing(..., uint256 value) public {
         *     token.safeTransferFrom(msg.sender, address(this), value);
         *     ...
         * }
         * ```
         *
         * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
         * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
         * {SafeERC20-safeTransferFrom}).
         *
         * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
         * contracts should have entry points that don't rely on permit.
         */
        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].
             *
             * CAUTION: See Security Considerations above.
             */
            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);
        }
        // SPDX-License-Identifier: MIT
        pragma solidity ^0.8.0;
        interface IDAIPermit {
            /// @param holder The address of the token owner.
            /// @param spender The address of the token spender.
            /// @param nonce The owner's nonce, increases at each call to permit.
            /// @param expiry The timestamp at which the permit is no longer valid.
            /// @param allowed Boolean that sets approval amount, true for type(uint256).max and false for 0.
            /// @param v Must produce valid secp256k1 signature from the owner along with r and s.
            /// @param r Must produce valid secp256k1 signature from the owner along with v and s.
            /// @param s Must produce valid secp256k1 signature from the owner along with r and v.
            function permit(
                address holder,
                address spender,
                uint256 nonce,
                uint256 expiry,
                bool allowed,
                uint8 v,
                bytes32 r,
                bytes32 s
            ) external;
        }
        // SPDX-License-Identifier: MIT
        pragma solidity ^0.8.0;
        import {IEIP712} from "./IEIP712.sol";
        /// @title AllowanceTransfer
        /// @notice Handles ERC20 token permissions through signature based allowance setting and ERC20 token transfers by checking allowed amounts
        /// @dev Requires user's token approval on the Permit2 contract
        interface IAllowanceTransfer is IEIP712 {
            /// @notice Thrown when an allowance on a token has expired.
            /// @param deadline The timestamp at which the allowed amount is no longer valid
            error AllowanceExpired(uint256 deadline);
            /// @notice Thrown when an allowance on a token has been depleted.
            /// @param amount The maximum amount allowed
            error InsufficientAllowance(uint256 amount);
            /// @notice Thrown when too many nonces are invalidated.
            error ExcessiveInvalidation();
            /// @notice Emits an event when the owner successfully invalidates an ordered nonce.
            event NonceInvalidation(
                address indexed owner, address indexed token, address indexed spender, uint48 newNonce, uint48 oldNonce
            );
            /// @notice Emits an event when the owner successfully sets permissions on a token for the spender.
            event Approval(
                address indexed owner, address indexed token, address indexed spender, uint160 amount, uint48 expiration
            );
            /// @notice Emits an event when the owner successfully sets permissions using a permit signature on a token for the spender.
            event Permit(
                address indexed owner,
                address indexed token,
                address indexed spender,
                uint160 amount,
                uint48 expiration,
                uint48 nonce
            );
            /// @notice Emits an event when the owner sets the allowance back to 0 with the lockdown function.
            event Lockdown(address indexed owner, address token, address spender);
            /// @notice The permit data for a token
            struct PermitDetails {
                // ERC20 token address
                address token;
                // the maximum amount allowed to spend
                uint160 amount;
                // timestamp at which a spender's token allowances become invalid
                uint48 expiration;
                // an incrementing value indexed per owner,token,and spender for each signature
                uint48 nonce;
            }
            /// @notice The permit message signed for a single token allowance
            struct PermitSingle {
                // the permit data for a single token alownce
                PermitDetails details;
                // address permissioned on the allowed tokens
                address spender;
                // deadline on the permit signature
                uint256 sigDeadline;
            }
            /// @notice The permit message signed for multiple token allowances
            struct PermitBatch {
                // the permit data for multiple token allowances
                PermitDetails[] details;
                // address permissioned on the allowed tokens
                address spender;
                // deadline on the permit signature
                uint256 sigDeadline;
            }
            /// @notice The saved permissions
            /// @dev This info is saved per owner, per token, per spender and all signed over in the permit message
            /// @dev Setting amount to type(uint160).max sets an unlimited approval
            struct PackedAllowance {
                // amount allowed
                uint160 amount;
                // permission expiry
                uint48 expiration;
                // an incrementing value indexed per owner,token,and spender for each signature
                uint48 nonce;
            }
            /// @notice A token spender pair.
            struct TokenSpenderPair {
                // the token the spender is approved
                address token;
                // the spender address
                address spender;
            }
            /// @notice Details for a token transfer.
            struct AllowanceTransferDetails {
                // the owner of the token
                address from;
                // the recipient of the token
                address to;
                // the amount of the token
                uint160 amount;
                // the token to be transferred
                address token;
            }
            /// @notice A mapping from owner address to token address to spender address to PackedAllowance struct, which contains details and conditions of the approval.
            /// @notice The mapping is indexed in the above order see: allowance[ownerAddress][tokenAddress][spenderAddress]
            /// @dev The packed slot holds the allowed amount, expiration at which the allowed amount is no longer valid, and current nonce thats updated on any signature based approvals.
            function allowance(address user, address token, address spender)
                external
                view
                returns (uint160 amount, uint48 expiration, uint48 nonce);
            /// @notice Approves the spender to use up to amount of the specified token up until the expiration
            /// @param token The token to approve
            /// @param spender The spender address to approve
            /// @param amount The approved amount of the token
            /// @param expiration The timestamp at which the approval is no longer valid
            /// @dev The packed allowance also holds a nonce, which will stay unchanged in approve
            /// @dev Setting amount to type(uint160).max sets an unlimited approval
            function approve(address token, address spender, uint160 amount, uint48 expiration) external;
            /// @notice Permit a spender to a given amount of the owners token via the owner's EIP-712 signature
            /// @dev May fail if the owner's nonce was invalidated in-flight by invalidateNonce
            /// @param owner The owner of the tokens being approved
            /// @param permitSingle Data signed over by the owner specifying the terms of approval
            /// @param signature The owner's signature over the permit data
            function permit(address owner, PermitSingle memory permitSingle, bytes calldata signature) external;
            /// @notice Permit a spender to the signed amounts of the owners tokens via the owner's EIP-712 signature
            /// @dev May fail if the owner's nonce was invalidated in-flight by invalidateNonce
            /// @param owner The owner of the tokens being approved
            /// @param permitBatch Data signed over by the owner specifying the terms of approval
            /// @param signature The owner's signature over the permit data
            function permit(address owner, PermitBatch memory permitBatch, bytes calldata signature) external;
            /// @notice Transfer approved tokens from one address to another
            /// @param from The address to transfer from
            /// @param to The address of the recipient
            /// @param amount The amount of the token to transfer
            /// @param token The token address to transfer
            /// @dev Requires the from address to have approved at least the desired amount
            /// of tokens to msg.sender.
            function transferFrom(address from, address to, uint160 amount, address token) external;
            /// @notice Transfer approved tokens in a batch
            /// @param transferDetails Array of owners, recipients, amounts, and tokens for the transfers
            /// @dev Requires the from addresses to have approved at least the desired amount
            /// of tokens to msg.sender.
            function transferFrom(AllowanceTransferDetails[] calldata transferDetails) external;
            /// @notice Enables performing a "lockdown" of the sender's Permit2 identity
            /// by batch revoking approvals
            /// @param approvals Array of approvals to revoke.
            function lockdown(TokenSpenderPair[] calldata approvals) external;
            /// @notice Invalidate nonces for a given (token, spender) pair
            /// @param token The token to invalidate nonces for
            /// @param spender The spender to invalidate nonces for
            /// @param newNonce The new nonce to set. Invalidates all nonces less than it.
            /// @dev Can't invalidate more than 2**16 nonces per transaction.
            function invalidateNonces(address token, address spender, uint48 newNonce) external;
        }
        // SPDX-License-Identifier: MIT
        pragma solidity ^0.8.17;
        library SafeCast160 {
            /// @notice Thrown when a valude greater than type(uint160).max is cast to uint160
            error UnsafeCast();
            /// @notice Safely casts uint256 to uint160
            /// @param value The uint256 to be cast
            function toUint160(uint256 value) internal pure returns (uint160) {
                if (value > type(uint160).max) revert UnsafeCast();
                return uint160(value);
            }
        }
        // SPDX-License-Identifier: MIT
        // OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol)
        pragma solidity ^0.8.20;
        /**
         * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
         * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
         * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
         * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
         *
         * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
         * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
         * case an upgrade adds a module that needs to be initialized.
         *
         * For example:
         *
         * [.hljs-theme-light.nopadding]
         * ```solidity
         * contract MyToken is ERC20Upgradeable {
         *     function initialize() initializer public {
         *         __ERC20_init("MyToken", "MTK");
         *     }
         * }
         *
         * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
         *     function initializeV2() reinitializer(2) public {
         *         __ERC20Permit_init("MyToken");
         *     }
         * }
         * ```
         *
         * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
         * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
         *
         * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
         * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
         *
         * [CAUTION]
         * ====
         * Avoid leaving a contract uninitialized.
         *
         * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
         * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
         * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
         *
         * [.hljs-theme-light.nopadding]
         * ```
         * /// @custom:oz-upgrades-unsafe-allow constructor
         * constructor() {
         *     _disableInitializers();
         * }
         * ```
         * ====
         */
        abstract contract Initializable {
            /**
             * @dev Storage of the initializable contract.
             *
             * It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
             * when using with upgradeable contracts.
             *
             * @custom:storage-location erc7201:openzeppelin.storage.Initializable
             */
            struct InitializableStorage {
                /**
                 * @dev Indicates that the contract has been initialized.
                 */
                uint64 _initialized;
                /**
                 * @dev Indicates that the contract is in the process of being initialized.
                 */
                bool _initializing;
            }
            // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
            bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;
            /**
             * @dev The contract is already initialized.
             */
            error InvalidInitialization();
            /**
             * @dev The contract is not initializing.
             */
            error NotInitializing();
            /**
             * @dev Triggered when the contract has been initialized or reinitialized.
             */
            event Initialized(uint64 version);
            /**
             * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
             * `onlyInitializing` functions can be used to initialize parent contracts.
             *
             * Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
             * number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
             * production.
             *
             * Emits an {Initialized} event.
             */
            modifier initializer() {
                // solhint-disable-next-line var-name-mixedcase
                InitializableStorage storage $ = _getInitializableStorage();
                // Cache values to avoid duplicated sloads
                bool isTopLevelCall = !$._initializing;
                uint64 initialized = $._initialized;
                // Allowed calls:
                // - initialSetup: the contract is not in the initializing state and no previous version was
                //                 initialized
                // - construction: the contract is initialized at version 1 (no reininitialization) and the
                //                 current contract is just being deployed
                bool initialSetup = initialized == 0 && isTopLevelCall;
                bool construction = initialized == 1 && address(this).code.length == 0;
                if (!initialSetup && !construction) {
                    revert InvalidInitialization();
                }
                $._initialized = 1;
                if (isTopLevelCall) {
                    $._initializing = true;
                }
                _;
                if (isTopLevelCall) {
                    $._initializing = false;
                    emit Initialized(1);
                }
            }
            /**
             * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
             * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
             * used to initialize parent contracts.
             *
             * A reinitializer may be used after the original initialization step. This is essential to configure modules that
             * are added through upgrades and that require initialization.
             *
             * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
             * cannot be nested. If one is invoked in the context of another, execution will revert.
             *
             * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
             * a contract, executing them in the right order is up to the developer or operator.
             *
             * WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.
             *
             * Emits an {Initialized} event.
             */
            modifier reinitializer(uint64 version) {
                // solhint-disable-next-line var-name-mixedcase
                InitializableStorage storage $ = _getInitializableStorage();
                if ($._initializing || $._initialized >= version) {
                    revert InvalidInitialization();
                }
                $._initialized = version;
                $._initializing = true;
                _;
                $._initializing = false;
                emit Initialized(version);
            }
            /**
             * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
             * {initializer} and {reinitializer} modifiers, directly or indirectly.
             */
            modifier onlyInitializing() {
                _checkInitializing();
                _;
            }
            /**
             * @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
             */
            function _checkInitializing() internal view virtual {
                if (!_isInitializing()) {
                    revert NotInitializing();
                }
            }
            /**
             * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
             * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
             * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
             * through proxies.
             *
             * Emits an {Initialized} event the first time it is successfully executed.
             */
            function _disableInitializers() internal virtual {
                // solhint-disable-next-line var-name-mixedcase
                InitializableStorage storage $ = _getInitializableStorage();
                if ($._initializing) {
                    revert InvalidInitialization();
                }
                if ($._initialized != type(uint64).max) {
                    $._initialized = type(uint64).max;
                    emit Initialized(type(uint64).max);
                }
            }
            /**
             * @dev Returns the highest version that has been initialized. See {reinitializer}.
             */
            function _getInitializedVersion() internal view returns (uint64) {
                return _getInitializableStorage()._initialized;
            }
            /**
             * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
             */
            function _isInitializing() internal view returns (bool) {
                return _getInitializableStorage()._initializing;
            }
            /**
             * @dev Returns a pointer to the storage namespace.
             */
            // solhint-disable-next-line var-name-mixedcase
            function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
                assembly {
                    $.slot := INITIALIZABLE_STORAGE
                }
            }
        }
        // SPDX-License-Identifier: MIT
        // OpenZeppelin Contracts (last updated v5.0.0) (utils/Context.sol)
        pragma solidity ^0.8.20;
        import {Initializable} from "../proxy/utils/Initializable.sol";
        /**
         * @dev Provides information about the current execution context, including the
         * sender of the transaction and its data. While these are generally available
         * via msg.sender and msg.data, they should not be accessed in such a direct
         * manner, since when dealing with meta-transactions the account sending and
         * paying for execution may not be the actual sender (as far as an application
         * is concerned).
         *
         * This contract is only required for intermediate, library-like contracts.
         */
        abstract contract ContextUpgradeable is Initializable {
            function __Context_init() internal onlyInitializing {
            }
            function __Context_init_unchained() internal onlyInitializing {
            }
            function _msgSender() internal view virtual returns (address) {
                return msg.sender;
            }
            function _msgData() internal view virtual returns (bytes calldata) {
                return msg.data;
            }
        }
        // SPDX-License-Identifier: MIT
        // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC6093.sol)
        pragma solidity ^0.8.20;
        /**
         * @dev Standard ERC20 Errors
         * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC20 tokens.
         */
        interface IERC20Errors {
            /**
             * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
             * @param sender Address whose tokens are being transferred.
             * @param balance Current balance for the interacting account.
             * @param needed Minimum amount required to perform a transfer.
             */
            error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);
            /**
             * @dev Indicates a failure with the token `sender`. Used in transfers.
             * @param sender Address whose tokens are being transferred.
             */
            error ERC20InvalidSender(address sender);
            /**
             * @dev Indicates a failure with the token `receiver`. Used in transfers.
             * @param receiver Address to which tokens are being transferred.
             */
            error ERC20InvalidReceiver(address receiver);
            /**
             * @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.
             * @param spender Address that may be allowed to operate on tokens without being their owner.
             * @param allowance Amount of tokens a `spender` is allowed to operate with.
             * @param needed Minimum amount required to perform a transfer.
             */
            error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);
            /**
             * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
             * @param approver Address initiating an approval operation.
             */
            error ERC20InvalidApprover(address approver);
            /**
             * @dev Indicates a failure with the `spender` to be approved. Used in approvals.
             * @param spender Address that may be allowed to operate on tokens without being their owner.
             */
            error ERC20InvalidSpender(address spender);
        }
        /**
         * @dev Standard ERC721 Errors
         * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC721 tokens.
         */
        interface IERC721Errors {
            /**
             * @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in EIP-20.
             * Used in balance queries.
             * @param owner Address of the current owner of a token.
             */
            error ERC721InvalidOwner(address owner);
            /**
             * @dev Indicates a `tokenId` whose `owner` is the zero address.
             * @param tokenId Identifier number of a token.
             */
            error ERC721NonexistentToken(uint256 tokenId);
            /**
             * @dev Indicates an error related to the ownership over a particular token. Used in transfers.
             * @param sender Address whose tokens are being transferred.
             * @param tokenId Identifier number of a token.
             * @param owner Address of the current owner of a token.
             */
            error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);
            /**
             * @dev Indicates a failure with the token `sender`. Used in transfers.
             * @param sender Address whose tokens are being transferred.
             */
            error ERC721InvalidSender(address sender);
            /**
             * @dev Indicates a failure with the token `receiver`. Used in transfers.
             * @param receiver Address to which tokens are being transferred.
             */
            error ERC721InvalidReceiver(address receiver);
            /**
             * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
             * @param operator Address that may be allowed to operate on tokens without being their owner.
             * @param tokenId Identifier number of a token.
             */
            error ERC721InsufficientApproval(address operator, uint256 tokenId);
            /**
             * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
             * @param approver Address initiating an approval operation.
             */
            error ERC721InvalidApprover(address approver);
            /**
             * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
             * @param operator Address that may be allowed to operate on tokens without being their owner.
             */
            error ERC721InvalidOperator(address operator);
        }
        /**
         * @dev Standard ERC1155 Errors
         * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC1155 tokens.
         */
        interface IERC1155Errors {
            /**
             * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
             * @param sender Address whose tokens are being transferred.
             * @param balance Current balance for the interacting account.
             * @param needed Minimum amount required to perform a transfer.
             * @param tokenId Identifier number of a token.
             */
            error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);
            /**
             * @dev Indicates a failure with the token `sender`. Used in transfers.
             * @param sender Address whose tokens are being transferred.
             */
            error ERC1155InvalidSender(address sender);
            /**
             * @dev Indicates a failure with the token `receiver`. Used in transfers.
             * @param receiver Address to which tokens are being transferred.
             */
            error ERC1155InvalidReceiver(address receiver);
            /**
             * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
             * @param operator Address that may be allowed to operate on tokens without being their owner.
             * @param owner Address of the current owner of a token.
             */
            error ERC1155MissingApprovalForAll(address operator, address owner);
            /**
             * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
             * @param approver Address initiating an approval operation.
             */
            error ERC1155InvalidApprover(address approver);
            /**
             * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
             * @param operator Address that may be allowed to operate on tokens without being their owner.
             */
            error ERC1155InvalidOperator(address operator);
            /**
             * @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.
             * Used in batch transfers.
             * @param idsLength Length of the array of token identifiers
             * @param valuesLength Length of the array of token amounts
             */
            error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
        }
        // SPDX-License-Identifier: MIT
        // OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)
        pragma solidity ^0.8.20;
        /**
         * @dev Collection of functions related to the address type
         */
        library Address {
            /**
             * @dev The ETH balance of the account is not enough to perform the operation.
             */
            error AddressInsufficientBalance(address account);
            /**
             * @dev There's no code at `target` (it is not a contract).
             */
            error AddressEmptyCode(address target);
            /**
             * @dev A call to an address target failed. The target may have reverted.
             */
            error FailedInnerCall();
            /**
             * @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://consensys.net/diligence/blog/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.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
             */
            function sendValue(address payable recipient, uint256 amount) internal {
                if (address(this).balance < amount) {
                    revert AddressInsufficientBalance(address(this));
                }
                (bool success, ) = recipient.call{value: amount}("");
                if (!success) {
                    revert FailedInnerCall();
                }
            }
            /**
             * @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 or custom error, it is bubbled
             * up by this function (like regular Solidity function calls). However, if
             * the call reverted with no returned reason, this function reverts with a
             * {FailedInnerCall} error.
             *
             * 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.
             */
            function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                return functionCallWithValue(target, data, 0);
            }
            /**
             * @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`.
             */
            function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
                if (address(this).balance < value) {
                    revert AddressInsufficientBalance(address(this));
                }
                (bool success, bytes memory returndata) = target.call{value: value}(data);
                return verifyCallResultFromTarget(target, success, returndata);
            }
            /**
             * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
             * but performing a static call.
             */
            function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                (bool success, bytes memory returndata) = target.staticcall(data);
                return verifyCallResultFromTarget(target, success, returndata);
            }
            /**
             * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
             * but performing a delegate call.
             */
            function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                (bool success, bytes memory returndata) = target.delegatecall(data);
                return verifyCallResultFromTarget(target, success, returndata);
            }
            /**
             * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
             * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
             * unsuccessful call.
             */
            function verifyCallResultFromTarget(
                address target,
                bool success,
                bytes memory returndata
            ) internal view returns (bytes memory) {
                if (!success) {
                    _revert(returndata);
                } else {
                    // only check if target is a contract if the call was successful and the return data is empty
                    // otherwise we already know that it was a contract
                    if (returndata.length == 0 && target.code.length == 0) {
                        revert AddressEmptyCode(target);
                    }
                    return returndata;
                }
            }
            /**
             * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
             * revert reason or with a default {FailedInnerCall} error.
             */
            function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
                if (!success) {
                    _revert(returndata);
                } else {
                    return returndata;
                }
            }
            /**
             * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
             */
            function _revert(bytes memory returndata) 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 FailedInnerCall();
                }
            }
        }
        // SPDX-License-Identifier: MIT
        pragma solidity ^0.8.0;
        interface IEIP712 {
            function DOMAIN_SEPARATOR() external view returns (bytes32);
        }
        

        File 4 of 4: StakedTokenV1
        /**
         * SPDX-License-Identifier: MIT
         *
         * Copyright (c) 2022 Coinbase, Inc.
         *
         * Permission is hereby granted, free of charge, to any person obtaining a copy
         * of this software and associated documentation files (the "Software"), to deal
         * in the Software without restriction, including without limitation the rights
         * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
         * copies of the Software, and to permit persons to whom the Software is
         * furnished to do so, subject to the following conditions:
         *
         * The above copyright notice and this permission notice shall be included in
         * copies or substantial portions of the Software.
         *
         * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
         * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
         * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
         * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
         * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
         * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
         * SOFTWARE.
         */
        pragma solidity 0.6.12;
        import { FiatTokenV2_1 } from "centre-tokens/contracts/v2/FiatTokenV2_1.sol";
        /**
         * @title StakedTokenV1
         * @notice ERC20 token backed by staked cryptocurrency reserves, version 1
         */
        contract StakedTokenV1 is FiatTokenV2_1 {
            /**
             * @dev Storage slot with the address of the current oracle.
             * This is the keccak-256 hash of "org.coinbase.stakedToken.exchangeRateOracle"
             */
            bytes32 private constant _EXCHANGE_RATE_ORACLE_POSITION = keccak256(
                "org.coinbase.stakedToken.exchangeRateOracle"
            );
            /**
             * @dev Storage slot with the current exchange rate.
             * This is the keccak-256 hash of "org.coinbase.stakedToken.exchangeRate"
             */
            bytes32 private constant _EXCHANGE_RATE_POSITION = keccak256(
                "org.coinbase.stakedToken.exchangeRate"
            );
            /**
             * @dev Emitted when the oracle is updated
             * @param newOracle The address of the new oracle
             */
            event OracleUpdated(address indexed newOracle);
            /**
             * @dev Emitted when the exchange rate is updated
             * @param oracle The address initiating the exchange rate update
             * @param newExchangeRate The new exchange rate
             */
            event ExchangeRateUpdated(address indexed oracle, uint256 newExchangeRate);
            /**
             * @dev Throws if called by any account other than the oracle
             */
            modifier onlyOracle() {
                require(
                    msg.sender == oracle(),
                    "StakedTokenV1: caller is not the oracle"
                );
                _;
            }
            /**
             * @dev Function to update the oracle
             * @param newOracle The new oracle
             */
            function updateOracle(address newOracle) external onlyOwner {
                require(
                    newOracle != address(0),
                    "StakedTokenV1: oracle is the zero address"
                );
                require(
                    newOracle != oracle(),
                    "StakedTokenV1: new oracle is already the oracle"
                );
                bytes32 position = _EXCHANGE_RATE_ORACLE_POSITION;
                assembly {
                    sstore(position, newOracle)
                }
                emit OracleUpdated(newOracle);
            }
            /**
             * @dev Function to update the exchange rate
             * @param newExchangeRate The new exchange rate
             */
            function updateExchangeRate(uint256 newExchangeRate) external onlyOracle {
                require(
                    newExchangeRate > 0,
                    "StakedTokenV1: new exchange rate cannot be 0"
                );
                bytes32 position = _EXCHANGE_RATE_POSITION;
                assembly {
                    sstore(position, newExchangeRate)
                }
                emit ExchangeRateUpdated(msg.sender, newExchangeRate);
            }
            /**
             * @dev Returns the address of the current oracle
             * @return _oracle The address of the oracle
             */
            function oracle() public view returns (address _oracle) {
                bytes32 position = _EXCHANGE_RATE_ORACLE_POSITION;
                assembly {
                    _oracle := sload(position)
                }
            }
            /**
             * @dev Returns the current exchange rate scaled by by 10**18
             * @return _exchangeRate The exchange rate
             */
            function exchangeRate() public view returns (uint256 _exchangeRate) {
                bytes32 position = _EXCHANGE_RATE_POSITION;
                assembly {
                    _exchangeRate := sload(position)
                }
            }
        }
        /**
         * SPDX-License-Identifier: MIT
         *
         * Copyright (c) 2018-2020 CENTRE SECZ
         *
         * Permission is hereby granted, free of charge, to any person obtaining a copy
         * of this software and associated documentation files (the "Software"), to deal
         * in the Software without restriction, including without limitation the rights
         * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
         * copies of the Software, and to permit persons to whom the Software is
         * furnished to do so, subject to the following conditions:
         *
         * The above copyright notice and this permission notice shall be included in
         * copies or substantial portions of the Software.
         *
         * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
         * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
         * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
         * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
         * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
         * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
         * SOFTWARE.
         */
        pragma solidity 0.6.12;
        import { FiatTokenV2 } from "./FiatTokenV2.sol";
        // solhint-disable func-name-mixedcase
        /**
         * @title FiatToken V2.1
         * @notice ERC20 Token backed by fiat reserves, version 2.1
         */
        contract FiatTokenV2_1 is FiatTokenV2 {
            /**
             * @notice Initialize v2.1
             * @param lostAndFound  The address to which the locked funds are sent
             */
            function initializeV2_1(address lostAndFound) external {
                // solhint-disable-next-line reason-string
                require(_initializedVersion == 1);
                uint256 lockedAmount = balances[address(this)];
                if (lockedAmount > 0) {
                    _transfer(address(this), lostAndFound, lockedAmount);
                }
                blacklisted[address(this)] = true;
                _initializedVersion = 2;
            }
            /**
             * @notice Version string for the EIP712 domain separator
             * @return Version string
             */
            function version() external view returns (string memory) {
                return "2";
            }
        }
        /**
         * SPDX-License-Identifier: MIT
         *
         * Copyright (c) 2018-2020 CENTRE SECZ
         *
         * Permission is hereby granted, free of charge, to any person obtaining a copy
         * of this software and associated documentation files (the "Software"), to deal
         * in the Software without restriction, including without limitation the rights
         * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
         * copies of the Software, and to permit persons to whom the Software is
         * furnished to do so, subject to the following conditions:
         *
         * The above copyright notice and this permission notice shall be included in
         * copies or substantial portions of the Software.
         *
         * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
         * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
         * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
         * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
         * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
         * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
         * SOFTWARE.
         */
        pragma solidity 0.6.12;
        import { FiatTokenV1_1 } from "../v1.1/FiatTokenV1_1.sol";
        import { AbstractFiatTokenV2 } from "./AbstractFiatTokenV2.sol";
        import { EIP712 } from "../util/EIP712.sol";
        import { EIP712Domain } from "./EIP712Domain.sol";
        import { EIP3009 } from "./EIP3009.sol";
        import { EIP2612 } from "./EIP2612.sol";
        /**
         * @title FiatToken V2
         * @notice ERC20 Token backed by fiat reserves, version 2
         */
        contract FiatTokenV2 is FiatTokenV1_1, EIP3009, EIP2612 {
            uint8 internal _initializedVersion;
            /**
             * @notice Initialize v2
             * @param newName   New token name
             */
            function initializeV2(string calldata newName) external {
                // solhint-disable-next-line reason-string
                require(initialized && _initializedVersion == 0);
                name = newName;
                DOMAIN_SEPARATOR = EIP712.makeDomainSeparator(newName, "2");
                _initializedVersion = 1;
            }
            /**
             * @notice Increase the allowance by a given increment
             * @param spender   Spender's address
             * @param increment Amount of increase in allowance
             * @return True if successful
             */
            function increaseAllowance(address spender, uint256 increment)
                external
                whenNotPaused
                notBlacklisted(msg.sender)
                notBlacklisted(spender)
                returns (bool)
            {
                _increaseAllowance(msg.sender, spender, increment);
                return true;
            }
            /**
             * @notice Decrease the allowance by a given decrement
             * @param spender   Spender's address
             * @param decrement Amount of decrease in allowance
             * @return True if successful
             */
            function decreaseAllowance(address spender, uint256 decrement)
                external
                whenNotPaused
                notBlacklisted(msg.sender)
                notBlacklisted(spender)
                returns (bool)
            {
                _decreaseAllowance(msg.sender, spender, decrement);
                return true;
            }
            /**
             * @notice Execute a transfer with a signed authorization
             * @param from          Payer's address (Authorizer)
             * @param to            Payee's address
             * @param value         Amount to be transferred
             * @param validAfter    The time after which this is valid (unix time)
             * @param validBefore   The time before which this is valid (unix time)
             * @param nonce         Unique nonce
             * @param v             v of the signature
             * @param r             r of the signature
             * @param s             s of the signature
             */
            function transferWithAuthorization(
                address from,
                address to,
                uint256 value,
                uint256 validAfter,
                uint256 validBefore,
                bytes32 nonce,
                uint8 v,
                bytes32 r,
                bytes32 s
            ) external whenNotPaused notBlacklisted(from) notBlacklisted(to) {
                _transferWithAuthorization(
                    from,
                    to,
                    value,
                    validAfter,
                    validBefore,
                    nonce,
                    v,
                    r,
                    s
                );
            }
            /**
             * @notice Receive a transfer with a signed authorization from the payer
             * @dev This has an additional check to ensure that the payee's address
             * matches the caller of this function to prevent front-running attacks.
             * @param from          Payer's address (Authorizer)
             * @param to            Payee's address
             * @param value         Amount to be transferred
             * @param validAfter    The time after which this is valid (unix time)
             * @param validBefore   The time before which this is valid (unix time)
             * @param nonce         Unique nonce
             * @param v             v of the signature
             * @param r             r of the signature
             * @param s             s of the signature
             */
            function receiveWithAuthorization(
                address from,
                address to,
                uint256 value,
                uint256 validAfter,
                uint256 validBefore,
                bytes32 nonce,
                uint8 v,
                bytes32 r,
                bytes32 s
            ) external whenNotPaused notBlacklisted(from) notBlacklisted(to) {
                _receiveWithAuthorization(
                    from,
                    to,
                    value,
                    validAfter,
                    validBefore,
                    nonce,
                    v,
                    r,
                    s
                );
            }
            /**
             * @notice Attempt to cancel an authorization
             * @dev Works only if the authorization is not yet used.
             * @param authorizer    Authorizer's address
             * @param nonce         Nonce of the authorization
             * @param v             v of the signature
             * @param r             r of the signature
             * @param s             s of the signature
             */
            function cancelAuthorization(
                address authorizer,
                bytes32 nonce,
                uint8 v,
                bytes32 r,
                bytes32 s
            ) external whenNotPaused {
                _cancelAuthorization(authorizer, nonce, v, r, s);
            }
            /**
             * @notice Update allowance with a signed permit
             * @param owner       Token owner's address (Authorizer)
             * @param spender     Spender's address
             * @param value       Amount of allowance
             * @param deadline    Expiration time, seconds since the epoch
             * @param v           v of the signature
             * @param r           r of the signature
             * @param s           s of the signature
             */
            function permit(
                address owner,
                address spender,
                uint256 value,
                uint256 deadline,
                uint8 v,
                bytes32 r,
                bytes32 s
            ) external whenNotPaused notBlacklisted(owner) notBlacklisted(spender) {
                _permit(owner, spender, value, deadline, v, r, s);
            }
            /**
             * @notice Internal function to increase the allowance by a given increment
             * @param owner     Token owner's address
             * @param spender   Spender's address
             * @param increment Amount of increase
             */
            function _increaseAllowance(
                address owner,
                address spender,
                uint256 increment
            ) internal override {
                _approve(owner, spender, allowed[owner][spender].add(increment));
            }
            /**
             * @notice Internal function to decrease the allowance by a given decrement
             * @param owner     Token owner's address
             * @param spender   Spender's address
             * @param decrement Amount of decrease
             */
            function _decreaseAllowance(
                address owner,
                address spender,
                uint256 decrement
            ) internal override {
                _approve(
                    owner,
                    spender,
                    allowed[owner][spender].sub(
                        decrement,
                        "ERC20: decreased allowance below zero"
                    )
                );
            }
        }
        /**
         * SPDX-License-Identifier: MIT
         *
         * Copyright (c) 2018-2020 CENTRE SECZ
         *
         * Permission is hereby granted, free of charge, to any person obtaining a copy
         * of this software and associated documentation files (the "Software"), to deal
         * in the Software without restriction, including without limitation the rights
         * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
         * copies of the Software, and to permit persons to whom the Software is
         * furnished to do so, subject to the following conditions:
         *
         * The above copyright notice and this permission notice shall be included in
         * copies or substantial portions of the Software.
         *
         * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
         * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
         * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
         * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
         * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
         * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
         * SOFTWARE.
         */
        pragma solidity 0.6.12;
        import { FiatTokenV1 } from "../v1/FiatTokenV1.sol";
        import { Rescuable } from "./Rescuable.sol";
        /**
         * @title FiatTokenV1_1
         * @dev ERC20 Token backed by fiat reserves
         */
        contract FiatTokenV1_1 is FiatTokenV1, Rescuable {
        }
        /**
         * SPDX-License-Identifier: MIT
         *
         * Copyright (c) 2018-2020 CENTRE SECZ
         *
         * Permission is hereby granted, free of charge, to any person obtaining a copy
         * of this software and associated documentation files (the "Software"), to deal
         * in the Software without restriction, including without limitation the rights
         * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
         * copies of the Software, and to permit persons to whom the Software is
         * furnished to do so, subject to the following conditions:
         *
         * The above copyright notice and this permission notice shall be included in
         * copies or substantial portions of the Software.
         *
         * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
         * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
         * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
         * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
         * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
         * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
         * SOFTWARE.
         */
        pragma solidity 0.6.12;
        import { AbstractFiatTokenV1 } from "../v1/AbstractFiatTokenV1.sol";
        abstract contract AbstractFiatTokenV2 is AbstractFiatTokenV1 {
            function _increaseAllowance(
                address owner,
                address spender,
                uint256 increment
            ) internal virtual;
            function _decreaseAllowance(
                address owner,
                address spender,
                uint256 decrement
            ) internal virtual;
        }
        /**
         * SPDX-License-Identifier: MIT
         *
         * Copyright (c) 2018-2020 CENTRE SECZ
         *
         * Permission is hereby granted, free of charge, to any person obtaining a copy
         * of this software and associated documentation files (the "Software"), to deal
         * in the Software without restriction, including without limitation the rights
         * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
         * copies of the Software, and to permit persons to whom the Software is
         * furnished to do so, subject to the following conditions:
         *
         * The above copyright notice and this permission notice shall be included in
         * copies or substantial portions of the Software.
         *
         * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
         * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
         * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
         * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
         * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
         * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
         * SOFTWARE.
         */
        pragma solidity 0.6.12;
        import { ECRecover } from "./ECRecover.sol";
        /**
         * @title EIP712
         * @notice A library that provides EIP712 helper functions
         */
        library EIP712 {
            /**
             * @notice Make EIP712 domain separator
             * @param name      Contract name
             * @param version   Contract version
             * @return Domain separator
             */
            function makeDomainSeparator(string memory name, string memory version)
                internal
                view
                returns (bytes32)
            {
                uint256 chainId;
                assembly {
                    chainId := chainid()
                }
                return
                    keccak256(
                        abi.encode(
                            // keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")
                            0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f,
                            keccak256(bytes(name)),
                            keccak256(bytes(version)),
                            chainId,
                            address(this)
                        )
                    );
            }
            /**
             * @notice Recover signer's address from a EIP712 signature
             * @param domainSeparator   Domain separator
             * @param v                 v of the signature
             * @param r                 r of the signature
             * @param s                 s of the signature
             * @param typeHashAndData   Type hash concatenated with data
             * @return Signer's address
             */
            function recover(
                bytes32 domainSeparator,
                uint8 v,
                bytes32 r,
                bytes32 s,
                bytes memory typeHashAndData
            ) internal pure returns (address) {
                bytes32 digest = keccak256(
                    abi.encodePacked(
                        "\\x19\\x01",
                        domainSeparator,
                        keccak256(typeHashAndData)
                    )
                );
                return ECRecover.recover(digest, v, r, s);
            }
        }
        /**
         * SPDX-License-Identifier: MIT
         *
         * Copyright (c) 2018-2020 CENTRE SECZ
         *
         * Permission is hereby granted, free of charge, to any person obtaining a copy
         * of this software and associated documentation files (the "Software"), to deal
         * in the Software without restriction, including without limitation the rights
         * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
         * copies of the Software, and to permit persons to whom the Software is
         * furnished to do so, subject to the following conditions:
         *
         * The above copyright notice and this permission notice shall be included in
         * copies or substantial portions of the Software.
         *
         * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
         * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
         * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
         * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
         * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
         * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
         * SOFTWARE.
         */
        pragma solidity 0.6.12;
        /**
         * @title EIP712 Domain
         */
        contract EIP712Domain {
            /**
             * @dev EIP712 Domain Separator
             */
            bytes32 public DOMAIN_SEPARATOR;
        }
        /**
         * SPDX-License-Identifier: MIT
         *
         * Copyright (c) 2018-2020 CENTRE SECZ
         *
         * Permission is hereby granted, free of charge, to any person obtaining a copy
         * of this software and associated documentation files (the "Software"), to deal
         * in the Software without restriction, including without limitation the rights
         * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
         * copies of the Software, and to permit persons to whom the Software is
         * furnished to do so, subject to the following conditions:
         *
         * The above copyright notice and this permission notice shall be included in
         * copies or substantial portions of the Software.
         *
         * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
         * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
         * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
         * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
         * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
         * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
         * SOFTWARE.
         */
        pragma solidity 0.6.12;
        import { AbstractFiatTokenV2 } from "./AbstractFiatTokenV2.sol";
        import { EIP712Domain } from "./EIP712Domain.sol";
        import { EIP712 } from "../util/EIP712.sol";
        /**
         * @title EIP-3009
         * @notice Provide internal implementation for gas-abstracted transfers
         * @dev Contracts that inherit from this must wrap these with publicly
         * accessible functions, optionally adding modifiers where necessary
         */
        abstract contract EIP3009 is AbstractFiatTokenV2, EIP712Domain {
            // keccak256("TransferWithAuthorization(address from,address to,uint256 value,uint256 validAfter,uint256 validBefore,bytes32 nonce)")
            bytes32
                public constant TRANSFER_WITH_AUTHORIZATION_TYPEHASH = 0x7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a2267;
            // keccak256("ReceiveWithAuthorization(address from,address to,uint256 value,uint256 validAfter,uint256 validBefore,bytes32 nonce)")
            bytes32
                public constant RECEIVE_WITH_AUTHORIZATION_TYPEHASH = 0xd099cc98ef71107a616c4f0f941f04c322d8e254fe26b3c6668db87aae413de8;
            // keccak256("CancelAuthorization(address authorizer,bytes32 nonce)")
            bytes32
                public constant CANCEL_AUTHORIZATION_TYPEHASH = 0x158b0a9edf7a828aad02f63cd515c68ef2f50ba807396f6d12842833a1597429;
            /**
             * @dev authorizer address => nonce => bool (true if nonce is used)
             */
            mapping(address => mapping(bytes32 => bool)) private _authorizationStates;
            event AuthorizationUsed(address indexed authorizer, bytes32 indexed nonce);
            event AuthorizationCanceled(
                address indexed authorizer,
                bytes32 indexed nonce
            );
            /**
             * @notice Returns the state of an authorization
             * @dev Nonces are randomly generated 32-byte data unique to the
             * authorizer's address
             * @param authorizer    Authorizer's address
             * @param nonce         Nonce of the authorization
             * @return True if the nonce is used
             */
            function authorizationState(address authorizer, bytes32 nonce)
                external
                view
                returns (bool)
            {
                return _authorizationStates[authorizer][nonce];
            }
            /**
             * @notice Execute a transfer with a signed authorization
             * @param from          Payer's address (Authorizer)
             * @param to            Payee's address
             * @param value         Amount to be transferred
             * @param validAfter    The time after which this is valid (unix time)
             * @param validBefore   The time before which this is valid (unix time)
             * @param nonce         Unique nonce
             * @param v             v of the signature
             * @param r             r of the signature
             * @param s             s of the signature
             */
            function _transferWithAuthorization(
                address from,
                address to,
                uint256 value,
                uint256 validAfter,
                uint256 validBefore,
                bytes32 nonce,
                uint8 v,
                bytes32 r,
                bytes32 s
            ) internal {
                _requireValidAuthorization(from, nonce, validAfter, validBefore);
                bytes memory data = abi.encode(
                    TRANSFER_WITH_AUTHORIZATION_TYPEHASH,
                    from,
                    to,
                    value,
                    validAfter,
                    validBefore,
                    nonce
                );
                require(
                    EIP712.recover(DOMAIN_SEPARATOR, v, r, s, data) == from,
                    "FiatTokenV2: invalid signature"
                );
                _markAuthorizationAsUsed(from, nonce);
                _transfer(from, to, value);
            }
            /**
             * @notice Receive a transfer with a signed authorization from the payer
             * @dev This has an additional check to ensure that the payee's address
             * matches the caller of this function to prevent front-running attacks.
             * @param from          Payer's address (Authorizer)
             * @param to            Payee's address
             * @param value         Amount to be transferred
             * @param validAfter    The time after which this is valid (unix time)
             * @param validBefore   The time before which this is valid (unix time)
             * @param nonce         Unique nonce
             * @param v             v of the signature
             * @param r             r of the signature
             * @param s             s of the signature
             */
            function _receiveWithAuthorization(
                address from,
                address to,
                uint256 value,
                uint256 validAfter,
                uint256 validBefore,
                bytes32 nonce,
                uint8 v,
                bytes32 r,
                bytes32 s
            ) internal {
                require(to == msg.sender, "FiatTokenV2: caller must be the payee");
                _requireValidAuthorization(from, nonce, validAfter, validBefore);
                bytes memory data = abi.encode(
                    RECEIVE_WITH_AUTHORIZATION_TYPEHASH,
                    from,
                    to,
                    value,
                    validAfter,
                    validBefore,
                    nonce
                );
                require(
                    EIP712.recover(DOMAIN_SEPARATOR, v, r, s, data) == from,
                    "FiatTokenV2: invalid signature"
                );
                _markAuthorizationAsUsed(from, nonce);
                _transfer(from, to, value);
            }
            /**
             * @notice Attempt to cancel an authorization
             * @param authorizer    Authorizer's address
             * @param nonce         Nonce of the authorization
             * @param v             v of the signature
             * @param r             r of the signature
             * @param s             s of the signature
             */
            function _cancelAuthorization(
                address authorizer,
                bytes32 nonce,
                uint8 v,
                bytes32 r,
                bytes32 s
            ) internal {
                _requireUnusedAuthorization(authorizer, nonce);
                bytes memory data = abi.encode(
                    CANCEL_AUTHORIZATION_TYPEHASH,
                    authorizer,
                    nonce
                );
                require(
                    EIP712.recover(DOMAIN_SEPARATOR, v, r, s, data) == authorizer,
                    "FiatTokenV2: invalid signature"
                );
                _authorizationStates[authorizer][nonce] = true;
                emit AuthorizationCanceled(authorizer, nonce);
            }
            /**
             * @notice Check that an authorization is unused
             * @param authorizer    Authorizer's address
             * @param nonce         Nonce of the authorization
             */
            function _requireUnusedAuthorization(address authorizer, bytes32 nonce)
                private
                view
            {
                require(
                    !_authorizationStates[authorizer][nonce],
                    "FiatTokenV2: authorization is used or canceled"
                );
            }
            /**
             * @notice Check that authorization is valid
             * @param authorizer    Authorizer's address
             * @param nonce         Nonce of the authorization
             * @param validAfter    The time after which this is valid (unix time)
             * @param validBefore   The time before which this is valid (unix time)
             */
            function _requireValidAuthorization(
                address authorizer,
                bytes32 nonce,
                uint256 validAfter,
                uint256 validBefore
            ) private view {
                require(
                    now > validAfter,
                    "FiatTokenV2: authorization is not yet valid"
                );
                require(now < validBefore, "FiatTokenV2: authorization is expired");
                _requireUnusedAuthorization(authorizer, nonce);
            }
            /**
             * @notice Mark an authorization as used
             * @param authorizer    Authorizer's address
             * @param nonce         Nonce of the authorization
             */
            function _markAuthorizationAsUsed(address authorizer, bytes32 nonce)
                private
            {
                _authorizationStates[authorizer][nonce] = true;
                emit AuthorizationUsed(authorizer, nonce);
            }
        }
        /**
         * SPDX-License-Identifier: MIT
         *
         * Copyright (c) 2018-2020 CENTRE SECZ
         *
         * Permission is hereby granted, free of charge, to any person obtaining a copy
         * of this software and associated documentation files (the "Software"), to deal
         * in the Software without restriction, including without limitation the rights
         * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
         * copies of the Software, and to permit persons to whom the Software is
         * furnished to do so, subject to the following conditions:
         *
         * The above copyright notice and this permission notice shall be included in
         * copies or substantial portions of the Software.
         *
         * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
         * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
         * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
         * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
         * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
         * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
         * SOFTWARE.
         */
        pragma solidity 0.6.12;
        import { AbstractFiatTokenV2 } from "./AbstractFiatTokenV2.sol";
        import { EIP712Domain } from "./EIP712Domain.sol";
        import { EIP712 } from "../util/EIP712.sol";
        /**
         * @title EIP-2612
         * @notice Provide internal implementation for gas-abstracted approvals
         */
        abstract contract EIP2612 is AbstractFiatTokenV2, EIP712Domain {
            // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)")
            bytes32
                public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;
            mapping(address => uint256) private _permitNonces;
            /**
             * @notice Nonces for permit
             * @param owner Token owner's address (Authorizer)
             * @return Next nonce
             */
            function nonces(address owner) external view returns (uint256) {
                return _permitNonces[owner];
            }
            /**
             * @notice Verify a signed approval permit and execute if valid
             * @param owner     Token owner's address (Authorizer)
             * @param spender   Spender's address
             * @param value     Amount of allowance
             * @param deadline  The time at which this expires (unix time)
             * @param v         v of the signature
             * @param r         r of the signature
             * @param s         s of the signature
             */
            function _permit(
                address owner,
                address spender,
                uint256 value,
                uint256 deadline,
                uint8 v,
                bytes32 r,
                bytes32 s
            ) internal {
                require(deadline >= now, "FiatTokenV2: permit is expired");
                bytes memory data = abi.encode(
                    PERMIT_TYPEHASH,
                    owner,
                    spender,
                    value,
                    _permitNonces[owner]++,
                    deadline
                );
                require(
                    EIP712.recover(DOMAIN_SEPARATOR, v, r, s, data) == owner,
                    "EIP2612: invalid signature"
                );
                _approve(owner, spender, value);
            }
        }
        /**
         * SPDX-License-Identifier: MIT
         *
         * Copyright (c) 2018-2020 CENTRE SECZ
         *
         * Permission is hereby granted, free of charge, to any person obtaining a copy
         * of this software and associated documentation files (the "Software"), to deal
         * in the Software without restriction, including without limitation the rights
         * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
         * copies of the Software, and to permit persons to whom the Software is
         * furnished to do so, subject to the following conditions:
         *
         * The above copyright notice and this permission notice shall be included in
         * copies or substantial portions of the Software.
         *
         * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
         * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
         * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
         * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
         * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
         * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
         * SOFTWARE.
         */
        pragma solidity 0.6.12;
        import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol";
        import { AbstractFiatTokenV1 } from "./AbstractFiatTokenV1.sol";
        import { Ownable } from "./Ownable.sol";
        import { Pausable } from "./Pausable.sol";
        import { Blacklistable } from "./Blacklistable.sol";
        /**
         * @title FiatToken
         * @dev ERC20 Token backed by fiat reserves
         */
        contract FiatTokenV1 is AbstractFiatTokenV1, Ownable, Pausable, Blacklistable {
            using SafeMath for uint256;
            string public name;
            string public symbol;
            uint8 public decimals;
            string public currency;
            address public masterMinter;
            bool internal initialized;
            mapping(address => uint256) internal balances;
            mapping(address => mapping(address => uint256)) internal allowed;
            uint256 internal totalSupply_ = 0;
            mapping(address => bool) internal minters;
            mapping(address => uint256) internal minterAllowed;
            event Mint(address indexed minter, address indexed to, uint256 amount);
            event Burn(address indexed burner, uint256 amount);
            event MinterConfigured(address indexed minter, uint256 minterAllowedAmount);
            event MinterRemoved(address indexed oldMinter);
            event MasterMinterChanged(address indexed newMasterMinter);
            function initialize(
                string memory tokenName,
                string memory tokenSymbol,
                string memory tokenCurrency,
                uint8 tokenDecimals,
                address newMasterMinter,
                address newPauser,
                address newBlacklister,
                address newOwner
            ) public {
                require(!initialized, "FiatToken: contract is already initialized");
                require(
                    newMasterMinter != address(0),
                    "FiatToken: new masterMinter is the zero address"
                );
                require(
                    newPauser != address(0),
                    "FiatToken: new pauser is the zero address"
                );
                require(
                    newBlacklister != address(0),
                    "FiatToken: new blacklister is the zero address"
                );
                require(
                    newOwner != address(0),
                    "FiatToken: new owner is the zero address"
                );
                name = tokenName;
                symbol = tokenSymbol;
                currency = tokenCurrency;
                decimals = tokenDecimals;
                masterMinter = newMasterMinter;
                pauser = newPauser;
                blacklister = newBlacklister;
                setOwner(newOwner);
                initialized = true;
            }
            /**
             * @dev Throws if called by any account other than a minter
             */
            modifier onlyMinters() {
                require(minters[msg.sender], "FiatToken: caller is not a minter");
                _;
            }
            /**
             * @dev Function to mint tokens
             * @param _to The address that will receive the minted tokens.
             * @param _amount The amount of tokens to mint. Must be less than or equal
             * to the minterAllowance of the caller.
             * @return A boolean that indicates if the operation was successful.
             */
            function mint(address _to, uint256 _amount)
                external
                whenNotPaused
                onlyMinters
                notBlacklisted(msg.sender)
                notBlacklisted(_to)
                returns (bool)
            {
                require(_to != address(0), "FiatToken: mint to the zero address");
                require(_amount > 0, "FiatToken: mint amount not greater than 0");
                uint256 mintingAllowedAmount = minterAllowed[msg.sender];
                require(
                    _amount <= mintingAllowedAmount,
                    "FiatToken: mint amount exceeds minterAllowance"
                );
                totalSupply_ = totalSupply_.add(_amount);
                balances[_to] = balances[_to].add(_amount);
                minterAllowed[msg.sender] = mintingAllowedAmount.sub(_amount);
                emit Mint(msg.sender, _to, _amount);
                emit Transfer(address(0), _to, _amount);
                return true;
            }
            /**
             * @dev Throws if called by any account other than the masterMinter
             */
            modifier onlyMasterMinter() {
                require(
                    msg.sender == masterMinter,
                    "FiatToken: caller is not the masterMinter"
                );
                _;
            }
            /**
             * @dev Get minter allowance for an account
             * @param minter The address of the minter
             */
            function minterAllowance(address minter) external view returns (uint256) {
                return minterAllowed[minter];
            }
            /**
             * @dev Checks if account is a minter
             * @param account The address to check
             */
            function isMinter(address account) external view returns (bool) {
                return minters[account];
            }
            /**
             * @notice Amount of remaining tokens spender is allowed to transfer on
             * behalf of the token owner
             * @param owner     Token owner's address
             * @param spender   Spender's address
             * @return Allowance amount
             */
            function allowance(address owner, address spender)
                external
                override
                view
                returns (uint256)
            {
                return allowed[owner][spender];
            }
            /**
             * @dev Get totalSupply of token
             */
            function totalSupply() external override view returns (uint256) {
                return totalSupply_;
            }
            /**
             * @dev Get token balance of an account
             * @param account address The account
             */
            function balanceOf(address account)
                external
                override
                view
                returns (uint256)
            {
                return balances[account];
            }
            /**
             * @notice Set spender's allowance over the caller's tokens to be a given
             * value.
             * @param spender   Spender's address
             * @param value     Allowance amount
             * @return True if successful
             */
            function approve(address spender, uint256 value)
                external
                override
                whenNotPaused
                notBlacklisted(msg.sender)
                notBlacklisted(spender)
                returns (bool)
            {
                _approve(msg.sender, spender, value);
                return true;
            }
            /**
             * @dev Internal function to set allowance
             * @param owner     Token owner's address
             * @param spender   Spender's address
             * @param value     Allowance amount
             */
            function _approve(
                address owner,
                address spender,
                uint256 value
            ) internal override {
                require(owner != address(0), "ERC20: approve from the zero address");
                require(spender != address(0), "ERC20: approve to the zero address");
                allowed[owner][spender] = value;
                emit Approval(owner, spender, value);
            }
            /**
             * @notice Transfer tokens by spending allowance
             * @param from  Payer's address
             * @param to    Payee's address
             * @param value Transfer amount
             * @return True if successful
             */
            function transferFrom(
                address from,
                address to,
                uint256 value
            )
                external
                override
                whenNotPaused
                notBlacklisted(msg.sender)
                notBlacklisted(from)
                notBlacklisted(to)
                returns (bool)
            {
                require(
                    value <= allowed[from][msg.sender],
                    "ERC20: transfer amount exceeds allowance"
                );
                _transfer(from, to, value);
                allowed[from][msg.sender] = allowed[from][msg.sender].sub(value);
                return true;
            }
            /**
             * @notice Transfer tokens from the caller
             * @param to    Payee's address
             * @param value Transfer amount
             * @return True if successful
             */
            function transfer(address to, uint256 value)
                external
                override
                whenNotPaused
                notBlacklisted(msg.sender)
                notBlacklisted(to)
                returns (bool)
            {
                _transfer(msg.sender, to, value);
                return true;
            }
            /**
             * @notice Internal function to process transfers
             * @param from  Payer's address
             * @param to    Payee's address
             * @param value Transfer amount
             */
            function _transfer(
                address from,
                address to,
                uint256 value
            ) internal override {
                require(from != address(0), "ERC20: transfer from the zero address");
                require(to != address(0), "ERC20: transfer to the zero address");
                require(
                    value <= balances[from],
                    "ERC20: transfer amount exceeds balance"
                );
                balances[from] = balances[from].sub(value);
                balances[to] = balances[to].add(value);
                emit Transfer(from, to, value);
            }
            /**
             * @dev Function to add/update a new minter
             * @param minter The address of the minter
             * @param minterAllowedAmount The minting amount allowed for the minter
             * @return True if the operation was successful.
             */
            function configureMinter(address minter, uint256 minterAllowedAmount)
                external
                whenNotPaused
                onlyMasterMinter
                returns (bool)
            {
                minters[minter] = true;
                minterAllowed[minter] = minterAllowedAmount;
                emit MinterConfigured(minter, minterAllowedAmount);
                return true;
            }
            /**
             * @dev Function to remove a minter
             * @param minter The address of the minter to remove
             * @return True if the operation was successful.
             */
            function removeMinter(address minter)
                external
                onlyMasterMinter
                returns (bool)
            {
                minters[minter] = false;
                minterAllowed[minter] = 0;
                emit MinterRemoved(minter);
                return true;
            }
            /**
             * @dev allows a minter to burn some of its own tokens
             * Validates that caller is a minter and that sender is not blacklisted
             * amount is less than or equal to the minter's account balance
             * @param _amount uint256 the amount of tokens to be burned
             */
            function burn(uint256 _amount)
                external
                whenNotPaused
                onlyMinters
                notBlacklisted(msg.sender)
            {
                uint256 balance = balances[msg.sender];
                require(_amount > 0, "FiatToken: burn amount not greater than 0");
                require(balance >= _amount, "FiatToken: burn amount exceeds balance");
                totalSupply_ = totalSupply_.sub(_amount);
                balances[msg.sender] = balance.sub(_amount);
                emit Burn(msg.sender, _amount);
                emit Transfer(msg.sender, address(0), _amount);
            }
            function updateMasterMinter(address _newMasterMinter) external onlyOwner {
                require(
                    _newMasterMinter != address(0),
                    "FiatToken: new masterMinter is the zero address"
                );
                masterMinter = _newMasterMinter;
                emit MasterMinterChanged(masterMinter);
            }
        }
        /**
         * SPDX-License-Identifier: MIT
         *
         * Copyright (c) 2018-2020 CENTRE SECZ
         *
         * Permission is hereby granted, free of charge, to any person obtaining a copy
         * of this software and associated documentation files (the "Software"), to deal
         * in the Software without restriction, including without limitation the rights
         * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
         * copies of the Software, and to permit persons to whom the Software is
         * furnished to do so, subject to the following conditions:
         *
         * The above copyright notice and this permission notice shall be included in
         * copies or substantial portions of the Software.
         *
         * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
         * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
         * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
         * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
         * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
         * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
         * SOFTWARE.
         */
        pragma solidity 0.6.12;
        import { Ownable } from "../v1/Ownable.sol";
        import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
        import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
        contract Rescuable is Ownable {
            using SafeERC20 for IERC20;
            address private _rescuer;
            event RescuerChanged(address indexed newRescuer);
            /**
             * @notice Returns current rescuer
             * @return Rescuer's address
             */
            function rescuer() external view returns (address) {
                return _rescuer;
            }
            /**
             * @notice Revert if called by any account other than the rescuer.
             */
            modifier onlyRescuer() {
                require(msg.sender == _rescuer, "Rescuable: caller is not the rescuer");
                _;
            }
            /**
             * @notice Rescue ERC20 tokens locked up in this contract.
             * @param tokenContract ERC20 token contract address
             * @param to        Recipient address
             * @param amount    Amount to withdraw
             */
            function rescueERC20(
                IERC20 tokenContract,
                address to,
                uint256 amount
            ) external onlyRescuer {
                tokenContract.safeTransfer(to, amount);
            }
            /**
             * @notice Assign the rescuer role to a given address.
             * @param newRescuer New rescuer's address
             */
            function updateRescuer(address newRescuer) external onlyOwner {
                require(
                    newRescuer != address(0),
                    "Rescuable: new rescuer is the zero address"
                );
                _rescuer = newRescuer;
                emit RescuerChanged(newRescuer);
            }
        }
        // SPDX-License-Identifier: MIT
        pragma solidity ^0.6.0;
        /**
         * @dev Wrappers over Solidity's arithmetic operations with added overflow
         * checks.
         *
         * Arithmetic operations in Solidity wrap on overflow. This can easily result
         * in bugs, because programmers usually assume that an overflow raises an
         * error, which is the standard behavior in high level programming languages.
         * `SafeMath` restores this intuition by reverting the transaction when an
         * operation overflows.
         *
         * Using this library instead of the unchecked operations eliminates an entire
         * class of bugs, so it's recommended to use it always.
         */
        library SafeMath {
            /**
             * @dev Returns the addition of two unsigned integers, 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) {
                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;
            }
        }
        /**
         * SPDX-License-Identifier: MIT
         *
         * Copyright (c) 2018-2020 CENTRE SECZ
         *
         * Permission is hereby granted, free of charge, to any person obtaining a copy
         * of this software and associated documentation files (the "Software"), to deal
         * in the Software without restriction, including without limitation the rights
         * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
         * copies of the Software, and to permit persons to whom the Software is
         * furnished to do so, subject to the following conditions:
         *
         * The above copyright notice and this permission notice shall be included in
         * copies or substantial portions of the Software.
         *
         * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
         * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
         * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
         * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
         * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
         * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
         * SOFTWARE.
         */
        pragma solidity 0.6.12;
        import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
        abstract contract AbstractFiatTokenV1 is IERC20 {
            function _approve(
                address owner,
                address spender,
                uint256 value
            ) internal virtual;
            function _transfer(
                address from,
                address to,
                uint256 value
            ) internal virtual;
        }
        /**
         * SPDX-License-Identifier: MIT
         *
         * Copyright (c) 2018 zOS Global Limited.
         * Copyright (c) 2018-2020 CENTRE SECZ
         *
         * Permission is hereby granted, free of charge, to any person obtaining a copy
         * of this software and associated documentation files (the "Software"), to deal
         * in the Software without restriction, including without limitation the rights
         * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
         * copies of the Software, and to permit persons to whom the Software is
         * furnished to do so, subject to the following conditions:
         *
         * The above copyright notice and this permission notice shall be included in
         * copies or substantial portions of the Software.
         *
         * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
         * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
         * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
         * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
         * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
         * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
         * SOFTWARE.
         */
        pragma solidity 0.6.12;
        /**
         * @notice The Ownable contract has an owner address, and provides basic
         * authorization control functions
         * @dev Forked from https://github.com/OpenZeppelin/openzeppelin-labs/blob/3887ab77b8adafba4a26ace002f3a684c1a3388b/upgradeability_ownership/contracts/ownership/Ownable.sol
         * Modifications:
         * 1. Consolidate OwnableStorage into this contract (7/13/18)
         * 2. Reformat, conform to Solidity 0.6 syntax, and add error messages (5/13/20)
         * 3. Make public functions external (5/27/20)
         */
        contract Ownable {
            // Owner of the contract
            address private _owner;
            /**
             * @dev Event to show ownership has been transferred
             * @param previousOwner representing the address of the previous owner
             * @param newOwner representing the address of the new owner
             */
            event OwnershipTransferred(address previousOwner, address newOwner);
            /**
             * @dev The constructor sets the original owner of the contract to the sender account.
             */
            constructor() public {
                setOwner(msg.sender);
            }
            /**
             * @dev Tells the address of the owner
             * @return the address of the owner
             */
            function owner() external view returns (address) {
                return _owner;
            }
            /**
             * @dev Sets a new owner address
             */
            function setOwner(address newOwner) internal {
                _owner = newOwner;
            }
            /**
             * @dev Throws if called by any account other than the owner.
             */
            modifier onlyOwner() {
                require(msg.sender == _owner, "Ownable: caller is not the owner");
                _;
            }
            /**
             * @dev Allows the current owner to transfer control of the contract to a newOwner.
             * @param newOwner The address to transfer ownership to.
             */
            function transferOwnership(address newOwner) external onlyOwner {
                require(
                    newOwner != address(0),
                    "Ownable: new owner is the zero address"
                );
                emit OwnershipTransferred(_owner, newOwner);
                setOwner(newOwner);
            }
        }
        /**
         * SPDX-License-Identifier: MIT
         *
         * Copyright (c) 2016 Smart Contract Solutions, Inc.
         * Copyright (c) 2018-2020 CENTRE SECZ0
         *
         * Permission is hereby granted, free of charge, to any person obtaining a copy
         * of this software and associated documentation files (the "Software"), to deal
         * in the Software without restriction, including without limitation the rights
         * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
         * copies of the Software, and to permit persons to whom the Software is
         * furnished to do so, subject to the following conditions:
         *
         * The above copyright notice and this permission notice shall be included in
         * copies or substantial portions of the Software.
         *
         * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
         * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
         * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
         * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
         * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
         * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
         * SOFTWARE.
         */
        pragma solidity 0.6.12;
        import { Ownable } from "./Ownable.sol";
        /**
         * @notice Base contract which allows children to implement an emergency stop
         * mechanism
         * @dev Forked from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/feb665136c0dae9912e08397c1a21c4af3651ef3/contracts/lifecycle/Pausable.sol
         * Modifications:
         * 1. Added pauser role, switched pause/unpause to be onlyPauser (6/14/2018)
         * 2. Removed whenNotPause/whenPaused from pause/unpause (6/14/2018)
         * 3. Removed whenPaused (6/14/2018)
         * 4. Switches ownable library to use ZeppelinOS (7/12/18)
         * 5. Remove constructor (7/13/18)
         * 6. Reformat, conform to Solidity 0.6 syntax and add error messages (5/13/20)
         * 7. Make public functions external (5/27/20)
         */
        contract Pausable is Ownable {
            event Pause();
            event Unpause();
            event PauserChanged(address indexed newAddress);
            address public pauser;
            bool public paused = false;
            /**
             * @dev Modifier to make a function callable only when the contract is not paused.
             */
            modifier whenNotPaused() {
                require(!paused, "Pausable: paused");
                _;
            }
            /**
             * @dev throws if called by any account other than the pauser
             */
            modifier onlyPauser() {
                require(msg.sender == pauser, "Pausable: caller is not the pauser");
                _;
            }
            /**
             * @dev called by the owner to pause, triggers stopped state
             */
            function pause() external onlyPauser {
                paused = true;
                emit Pause();
            }
            /**
             * @dev called by the owner to unpause, returns to normal state
             */
            function unpause() external onlyPauser {
                paused = false;
                emit Unpause();
            }
            /**
             * @dev update the pauser role
             */
            function updatePauser(address _newPauser) external onlyOwner {
                require(
                    _newPauser != address(0),
                    "Pausable: new pauser is the zero address"
                );
                pauser = _newPauser;
                emit PauserChanged(pauser);
            }
        }
        /**
         * SPDX-License-Identifier: MIT
         *
         * Copyright (c) 2018-2020 CENTRE SECZ
         *
         * Permission is hereby granted, free of charge, to any person obtaining a copy
         * of this software and associated documentation files (the "Software"), to deal
         * in the Software without restriction, including without limitation the rights
         * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
         * copies of the Software, and to permit persons to whom the Software is
         * furnished to do so, subject to the following conditions:
         *
         * The above copyright notice and this permission notice shall be included in
         * copies or substantial portions of the Software.
         *
         * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
         * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
         * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
         * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
         * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
         * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
         * SOFTWARE.
         */
        pragma solidity 0.6.12;
        import { Ownable } from "./Ownable.sol";
        /**
         * @title Blacklistable Token
         * @dev Allows accounts to be blacklisted by a "blacklister" role
         */
        contract Blacklistable is Ownable {
            address public blacklister;
            mapping(address => bool) internal blacklisted;
            event Blacklisted(address indexed _account);
            event UnBlacklisted(address indexed _account);
            event BlacklisterChanged(address indexed newBlacklister);
            /**
             * @dev Throws if called by any account other than the blacklister
             */
            modifier onlyBlacklister() {
                require(
                    msg.sender == blacklister,
                    "Blacklistable: caller is not the blacklister"
                );
                _;
            }
            /**
             * @dev Throws if argument account is blacklisted
             * @param _account The address to check
             */
            modifier notBlacklisted(address _account) {
                require(
                    !blacklisted[_account],
                    "Blacklistable: account is blacklisted"
                );
                _;
            }
            /**
             * @dev Checks if account is blacklisted
             * @param _account The address to check
             */
            function isBlacklisted(address _account) external view returns (bool) {
                return blacklisted[_account];
            }
            /**
             * @dev Adds account to blacklist
             * @param _account The address to blacklist
             */
            function blacklist(address _account) external onlyBlacklister {
                blacklisted[_account] = true;
                emit Blacklisted(_account);
            }
            /**
             * @dev Removes account from blacklist
             * @param _account The address to remove from the blacklist
             */
            function unBlacklist(address _account) external onlyBlacklister {
                blacklisted[_account] = false;
                emit UnBlacklisted(_account);
            }
            function updateBlacklister(address _newBlacklister) external onlyOwner {
                require(
                    _newBlacklister != address(0),
                    "Blacklistable: new blacklister is the zero address"
                );
                blacklister = _newBlacklister;
                emit BlacklisterChanged(blacklister);
            }
        }
        // SPDX-License-Identifier: MIT
        pragma solidity ^0.6.0;
        /**
         * @dev Interface of the ERC20 standard as defined in the EIP.
         */
        interface IERC20 {
            /**
             * @dev Returns the amount of tokens in existence.
             */
            function totalSupply() external view returns (uint256);
            /**
             * @dev Returns the amount of tokens owned by `account`.
             */
            function balanceOf(address account) external view returns (uint256);
            /**
             * @dev Moves `amount` tokens from the caller's account to `recipient`.
             *
             * Returns a boolean value indicating whether the operation succeeded.
             *
             * Emits a {Transfer} event.
             */
            function transfer(address recipient, uint256 amount) external returns (bool);
            /**
             * @dev Returns the remaining number of tokens that `spender` will be
             * allowed to spend on behalf of `owner` through {transferFrom}. This is
             * zero by default.
             *
             * This value changes when {approve} or {transferFrom} are called.
             */
            function allowance(address owner, address spender) external view returns (uint256);
            /**
             * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
             *
             * Returns a boolean value indicating whether the operation succeeded.
             *
             * IMPORTANT: Beware that changing an allowance with this method brings the risk
             * that someone may use both the old and the new allowance by unfortunate
             * transaction ordering. One possible solution to mitigate this race
             * condition is to first reduce the spender's allowance to 0 and set the
             * desired value afterwards:
             * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
             *
             * Emits an {Approval} event.
             */
            function approve(address spender, uint256 amount) external returns (bool);
            /**
             * @dev Moves `amount` tokens from `sender` to `recipient` using the
             * allowance mechanism. `amount` is then deducted from the caller's
             * allowance.
             *
             * Returns a boolean value indicating whether the operation succeeded.
             *
             * Emits a {Transfer} event.
             */
            function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
            /**
             * @dev Emitted when `value` tokens are moved from one account (`from`) to
             * another (`to`).
             *
             * Note that `value` may be zero.
             */
            event Transfer(address indexed from, address indexed to, uint256 value);
            /**
             * @dev Emitted when the allowance of a `spender` for an `owner` is set by
             * a call to {approve}. `value` is the new allowance.
             */
            event Approval(address indexed owner, address indexed spender, uint256 value);
        }
        // SPDX-License-Identifier: MIT
        pragma solidity ^0.6.0;
        import "./IERC20.sol";
        import "../../math/SafeMath.sol";
        import "../../utils/Address.sol";
        /**
         * @title SafeERC20
         * @dev Wrappers around ERC20 operations that throw on failure (when the token
         * contract returns false). Tokens that return no value (and instead revert or
         * throw on failure) are also supported, non-reverting calls are assumed to be
         * successful.
         * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
         * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
         */
        library SafeERC20 {
            using SafeMath for uint256;
            using Address for address;
            function safeTransfer(IERC20 token, address to, uint256 value) internal {
                _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
            }
            function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
                _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
            }
            /**
             * @dev Deprecated. This function has issues similar to the ones found in
             * {IERC20-approve}, and its usage is discouraged.
             *
             * Whenever possible, use {safeIncreaseAllowance} and
             * {safeDecreaseAllowance} instead.
             */
            function safeApprove(IERC20 token, address spender, uint256 value) internal {
                // safeApprove should only be called when setting an initial allowance,
                // or when resetting it to zero. To increase and decrease it, use
                // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
                // solhint-disable-next-line max-line-length
                require((value == 0) || (token.allowance(address(this), spender) == 0),
                    "SafeERC20: approve from non-zero to non-zero allowance"
                );
                _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
            }
            function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
                uint256 newAllowance = token.allowance(address(this), spender).add(value);
                _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
            }
            function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
                uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero");
                _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
            }
            /**
             * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
             * on the return value: the return value is optional (but if data is returned, it must not be false).
             * @param token The token targeted by the call.
             * @param data The call data (encoded using abi.encode or one of its variants).
             */
            function _callOptionalReturn(IERC20 token, bytes memory data) private {
                // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
                // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
                // the target address contains contract code and also asserts for success in the low-level call.
                bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
                if (returndata.length > 0) { // Return data is optional
                    // solhint-disable-next-line max-line-length
                    require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
                }
            }
        }
        // SPDX-License-Identifier: MIT
        pragma solidity ^0.6.2;
        /**
         * @dev Collection of functions related to the address type
         */
        library Address {
            /**
             * @dev Returns true if `account` is a contract.
             *
             * [IMPORTANT]
             * ====
             * It is unsafe to assume that an address for which this function returns
             * false is an externally-owned account (EOA) and not a contract.
             *
             * Among others, `isContract` will return false for the following
             * types of addresses:
             *
             *  - an externally-owned account
             *  - a contract in construction
             *  - an address where a contract will be created
             *  - an address where a contract lived, but was destroyed
             * ====
             */
            function isContract(address account) internal view returns (bool) {
                // According to EIP-1052, 0x0 is the value returned for not-yet created accounts
                // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
                // for accounts without code, i.e. `keccak256('')`
                bytes32 codehash;
                bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
                // solhint-disable-next-line no-inline-assembly
                assembly { codehash := extcodehash(account) }
                return (codehash != accountHash && codehash != 0x0);
            }
            /**
             * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
             * `recipient`, forwarding all available gas and reverting on errors.
             *
             * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
             * of certain opcodes, possibly making contracts go over the 2300 gas limit
             * imposed by `transfer`, making them unable to receive funds via
             * `transfer`. {sendValue} removes this limitation.
             *
             * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
             *
             * IMPORTANT: because control is transferred to `recipient`, care must be
             * taken to not create reentrancy vulnerabilities. Consider using
             * {ReentrancyGuard} or the
             * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
             */
            function sendValue(address payable recipient, uint256 amount) internal {
                require(address(this).balance >= amount, "Address: insufficient balance");
                // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
                (bool success, ) = recipient.call{ value: amount }("");
                require(success, "Address: unable to send value, recipient may have reverted");
            }
            /**
             * @dev Performs a Solidity function call using a low level `call`. A
             * plain`call` is an unsafe replacement for a function call: use this
             * function instead.
             *
             * If `target` reverts with a revert reason, it is bubbled up by this
             * function (like regular Solidity function calls).
             *
             * Returns the raw returned data. To convert to the expected return value,
             * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
             *
             * Requirements:
             *
             * - `target` must be a contract.
             * - calling `target` with `data` must not revert.
             *
             * _Available since v3.1._
             */
            function functionCall(address target, bytes memory data) internal returns (bytes memory) {
              return functionCall(target, data, "Address: low-level call failed");
            }
            /**
             * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
             * `errorMessage` as a fallback revert reason when `target` reverts.
             *
             * _Available since v3.1._
             */
            function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
                return _functionCallWithValue(target, data, 0, errorMessage);
            }
            /**
             * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
             * but also transferring `value` wei to `target`.
             *
             * Requirements:
             *
             * - the calling contract must have an ETH balance of at least `value`.
             * - the called Solidity function must be `payable`.
             *
             * _Available since v3.1._
             */
            function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
                return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
            }
            /**
             * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
             * with `errorMessage` as a fallback revert reason when `target` reverts.
             *
             * _Available since v3.1._
             */
            function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
                require(address(this).balance >= value, "Address: insufficient balance for call");
                return _functionCallWithValue(target, data, value, errorMessage);
            }
            function _functionCallWithValue(address target, bytes memory data, uint256 weiValue, string memory errorMessage) private returns (bytes memory) {
                require(isContract(target), "Address: call to non-contract");
                // solhint-disable-next-line avoid-low-level-calls
                (bool success, bytes memory returndata) = target.call{ value: weiValue }(data);
                if (success) {
                    return returndata;
                } else {
                    // Look for revert reason and bubble it up if present
                    if (returndata.length > 0) {
                        // The easiest way to bubble the revert reason is using memory via assembly
                        // solhint-disable-next-line no-inline-assembly
                        assembly {
                            let returndata_size := mload(returndata)
                            revert(add(32, returndata), returndata_size)
                        }
                    } else {
                        revert(errorMessage);
                    }
                }
            }
        }
        /**
         * SPDX-License-Identifier: MIT
         *
         * Copyright (c) 2016-2019 zOS Global Limited
         * Copyright (c) 2018-2020 CENTRE SECZ
         *
         * Permission is hereby granted, free of charge, to any person obtaining a copy
         * of this software and associated documentation files (the "Software"), to deal
         * in the Software without restriction, including without limitation the rights
         * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
         * copies of the Software, and to permit persons to whom the Software is
         * furnished to do so, subject to the following conditions:
         *
         * The above copyright notice and this permission notice shall be included in
         * copies or substantial portions of the Software.
         *
         * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
         * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
         * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
         * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
         * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
         * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
         * SOFTWARE.
         */
        pragma solidity 0.6.12;
        /**
         * @title ECRecover
         * @notice A library that provides a safe ECDSA recovery function
         */
        library ECRecover {
            /**
             * @notice Recover signer's address from a signed message
             * @dev Adapted from: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/65e4ffde586ec89af3b7e9140bdc9235d1254853/contracts/cryptography/ECDSA.sol
             * Modifications: Accept v, r, and s as separate arguments
             * @param digest    Keccak-256 hash digest of the signed message
             * @param v         v of the signature
             * @param r         r of the signature
             * @param s         s of the signature
             * @return Signer address
             */
            function recover(
                bytes32 digest,
                uint8 v,
                bytes32 r,
                bytes32 s
            ) internal pure returns (address) {
                // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
                // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
                // the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most
                // signatures from current libraries generate a unique signature with an s-value in the lower half order.
                //
                // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
                // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
                // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
                // these malleable signatures as well.
                if (
                    uint256(s) >
                    0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0
                ) {
                    revert("ECRecover: invalid signature 's' value");
                }
                if (v != 27 && v != 28) {
                    revert("ECRecover: invalid signature 'v' value");
                }
                // If the signature is valid (and not malleable), return the signer address
                address signer = ecrecover(digest, v, r, s);
                require(signer != address(0), "ECRecover: invalid signature");
                return signer;
            }
        }