{"file_path":"contracts/LetterOfCredit.sol","creation_status":"success","source_code":"// SPDX-License-Identifier: ISC\npragma solidity 0.8.25;\n\nimport \"./interfaces/ICollateral.sol\";\nimport \"./interfaces/ILetterOfCredit.sol\";\nimport \"./interfaces/ILiquidator.sol\";\nimport \"./interfaces/ILiquidatable.sol\";\nimport {LetterOfCreditStorage} from \"./LetterOfCreditStorage.sol\";\nimport \"./Refundable.sol\";\nimport {SignatureNoncesUpgradeable} from \"./SignatureNoncesUpgradeable.sol\";\n\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {SafeERC20} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport {IERC165} from \"@openzeppelin/contracts/utils/introspection/IERC165.sol\";\nimport {SignatureChecker} from \"@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol\";\nimport {EIP712Upgradeable} from \"@openzeppelin/contracts-upgradeable/utils/cryptography/EIP712Upgradeable.sol\";\nimport {ReentrancyGuardUpgradeable} from \"@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol\";\nimport {Ownable2StepUpgradeable} from \"@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol\";\n\n/**\n * @title Contract for the creation, management, and redemption of collateralized Letters of Credit (LOCs) between parties.\n *\n * @dev Note: This contract follows the Initializable pattern and is only usable via delegate call.\n * @dev Contract Version: 3.0.0\n * @dev Version 3.0.0:\n *  - `tag` on createStaticLOC/createDynamicLOC is dynamic-length bytes (max MAX_TAG_BYTES), emitted\n *    via the new LOCCreatedV3 event.\n *  - Collateral factors are global: the per-LOC collateralFactorBasisPoints /\n *    liquidatorIncentiveBasisPoints snapshots are no longer written (or read) — all health and fee\n *    calculations use the pair's current CollateralFactor config.\n *  - New CollateralFactor field redeemBufferBasisPoints: the fee charged when a HEALTHY LOC's\n *    collateral is liquidated to serve a redemption; unhealthy liquidations continue to pay\n *    liquidatorIncentiveBasisPoints.\n *  - convertLOC() is restricted to UNHEALTHY LOCs: the creator's ability to voluntarily convert a\n *    healthy LOC (and the creator-authorization machinery around it) is removed. Healthy LOCs are\n *    liquidated exclusively through redeemLOC(). The _creatorAuthorization parameter is retained\n *    but ignored so the ILiquidatable ABI is unchanged for existing liquidation infrastructure.\n */\ncontract LetterOfCredit is\n  LetterOfCreditStorage,\n  ILetterOfCredit,\n  ILiquidatable,\n  Ownable2StepUpgradeable,\n  ReentrancyGuardUpgradeable,\n  Refundable,\n  EIP712Upgradeable,\n  SignatureNoncesUpgradeable\n{\n  using SafeERC20 for IERC20;\n\n  /*************\n   * CONSTANTS *\n   *************/\n\n  /// The maximum byte length of a LOC tag. Tags are event-only (never stored), so this bounds log\n  /// size while allowing multiple concatenated values (e.g. sixteen 32-byte file hashes).\n  uint256 public constant MAX_TAG_BYTES = 512;\n\n  /// EIP-712 type hash for cancelLOC approval signatures if the authorized party wishes to allow others execute.\n  bytes32 public constant CANCEL_TYPEHASH = keccak256(\"CancelAuthorization(uint96 locId,uint256 approverNonce)\");\n  /// EIP-712 type hash for redeemLOC approval signatures if the authorized party wishes to allow others execute.\n  bytes32 public constant REDEEM_TYPEHASH =\n    keccak256(\n      \"RedeemAuthorization(uint96 locId,uint256 creditedAmountToRedeem,uint256 creditedTokenAmount,address destinationAddress,uint256 approverNonce)\"\n    );\n\n  /***********\n   * GETTERS *\n   ***********/\n\n  /// Getter for locs so that contract callers may get strongly-typed LOCs.\n  function getLOC(uint96 _id) public view returns (LOC memory) {\n    return locs[_id];\n  }\n\n  /// Getter for creditedTokens so that contract callers may get strongly-typed CreditedTokens.\n  function getCreditedToken(address _address) public view returns (CreditedToken memory) {\n    return creditedTokens[_address];\n  }\n\n  /// Getter for collateralToCreditedToCollateralFactors so that contract callers may get strongly-typed CollateralFactors.\n  function getCollateralFactor(\n    address _collateralTokenAddress,\n    address _creditedTokenAddress\n  ) public view returns (CollateralFactor memory) {\n    return collateralToCreditedToCollateralFactors[_collateralTokenAddress][_creditedTokenAddress];\n  }\n\n  /*********************\n   * TRANSIENT STRUCTS *\n   *********************/\n\n  struct CreditedTokenConfig {\n    address tokenAddress;\n    uint256 minPerDynamicLOC;\n    uint256 maxPerDynamicLOC;\n    uint256 globalMaxInDynamicUse;\n  }\n\n  struct AssetPairCollateralFactor {\n    address collateralTokenAddress;\n    address creditedTokenAddress;\n    CollateralFactor collateralFactor;\n  }\n\n  struct LiquidationContext {\n    bool locUnhealthy;\n    uint256 creditedTokenAmountToReceive;\n    uint256 liquidationAmount;\n    uint256 liquidatorFeeAmount;\n    uint256 collateralToClaimAndSendLiquidator;\n  }\n\n  /*************\n   * FUNCTIONS *\n   *************/\n\n  /// Make it so this contract cannot be initialized and can only be used via delegatecall.\n  constructor() {\n    _disableInitializers();\n  }\n\n  /**\n   * @notice Initializes the `LetterOfCredit` contract, setting the necessary configuration parameters defining how it may\n   * be used.\n   * @param _owner The address to configure as the initial owner.\n   * @param _collateralContract The ICollateral contract to use for collateral.\n   * @param _priceOracle The IPriceOracle contract to use for oracle prices.\n   * @param _maxPriceUpdateSecondsAgo The maximum age, in seconds, of an oracle price update that is valid for\n   * operations requiring oracle prices.\n   * @param _maxLocDurationSeconds The maximum time until expiration a LOC may have at any given time.\n   * @param _creditedTokens The tokens to support as the Credited Token for Letters of Credit.\n   * @param _assetPairCollateralFactors The asset pair collateral factors.\n   */\n  function initialize(\n    address _owner,\n    ICollateral _collateralContract,\n    IPriceOracle _priceOracle,\n    uint16 _maxPriceUpdateSecondsAgo,\n    uint32 _maxLocDurationSeconds,\n    CreditedTokenConfig[] memory _creditedTokens,\n    AssetPairCollateralFactor[] memory _assetPairCollateralFactors\n  ) external initializer {\n    __Ownable_init(_owner);\n    __EIP712_init(\"LetterOfCredit\", \"1\");\n    __ReentrancyGuard_init();\n\n    _upsertCreditedTokensAsOwner(_creditedTokens);\n    _upsertCollateralFactorsAsOwner(_assetPairCollateralFactors);\n\n    collateralContract = _collateralContract;\n    priceOracle = _priceOracle;\n\n    maxLocDurationSeconds = _maxLocDurationSeconds;\n    maxPriceUpdateSecondsAgo = _maxPriceUpdateSecondsAgo;\n  }\n\n  /**\n   * @notice Creates a LOC with the caller as the creator.\n   * @param _beneficiary The beneficiary of the LOC.\n   * @param _collateralTokenAddress The token address of the collateral token.\n   * @param _collateralTokenAmount The amount of collateral to be locked.\n   * @param _creditedTokenAddress The token address of the credited token.\n   * @param _creditedTokenAmount The face value amount of the LOC.\n   * @param _expirationTimestamp The expiration time of the LOC.\n   * @param _oraclePriceUpdate (optional) The opaque bytes of the oracle price update to be processed. If not\n   * provided, the existing oracle price must not be stale, or the transaction will revert.\n   * @param _collateralizableAllowanceSignature (optional) The signature to allow this collateralizable to reserve the\n   * specified amount of the calling account's collateral within the `ICollateral` contract.\n   * @param _tag (optional) The optional tag to be used to associate this LOC with the off-chain purpose of this LOC.\n   * May be up to MAX_TAG_BYTES bytes (e.g. multiple concatenated identifiers or file hashes).\n   */\n  function createDynamicLOC(\n    address _beneficiary,\n    address _collateralTokenAddress,\n    uint256 _collateralTokenAmount,\n    address _creditedTokenAddress,\n    uint256 _creditedTokenAmount,\n    uint32 _expirationTimestamp,\n    bytes calldata _oraclePriceUpdate,\n    bytes calldata _collateralizableAllowanceSignature,\n    bytes calldata _tag\n  ) external payable refundExcess nonReentrant returns (uint96) {\n    if (_collateralTokenAddress == _creditedTokenAddress) revert InvalidLOCParameters();\n    if (_tag.length > MAX_TAG_BYTES) revert TagTooLong(MAX_TAG_BYTES, _tag.length);\n\n    if (_collateralizableAllowanceSignature.length > 0) {\n      _tryModifyCollateralizableAllowanceWithSignature(\n        collateralContract,\n        _collateralTokenAddress,\n        Pricing.safeCastToInt256(_collateralTokenAmount),\n        _collateralizableAllowanceSignature\n      );\n    }\n\n    Pricing.OraclePrice memory price;\n\n    if (_oraclePriceUpdate.length > 0) {\n      price = priceOracle.updatePrice{value: msg.value}(\n        _collateralTokenAddress,\n        _creditedTokenAddress,\n        _oraclePriceUpdate\n      );\n    } else {\n      price = priceOracle.getPrice(_collateralTokenAddress, _creditedTokenAddress);\n    }\n\n    return\n      _createDynamicLOC(\n        msg.sender,\n        _beneficiary,\n        _collateralTokenAddress,\n        _collateralTokenAmount,\n        _creditedTokenAddress,\n        _creditedTokenAmount,\n        _expirationTimestamp,\n        price,\n        _tag\n      );\n  }\n\n  /**\n   * @notice Creates a LOC with the caller as the creator.\n   * @param _beneficiary The beneficiary of the LOC.\n   * @param _tokenAddress The collateral/credited token address of the LOC.\n   * @param _tokenAmount The amount of the LOC. Note: more than this will be reserved if there is a claim fee.\n   * @param _expirationTimestamp The expiration time of the LOC.\n   * @param _collateralizableAllowanceSignature (optional) The signature to allow this collateralizable to reserve the\n   * specified amount of the calling account's collateral within the `ICollateral` contract.\n   * @param _tag (optional) The optional tag to be used to associate this LOC with the off-chain purpose of this LOC.\n   * May be up to MAX_TAG_BYTES bytes (e.g. multiple concatenated identifiers or file hashes).\n   */\n  function createStaticLOC(\n    address _beneficiary,\n    address _tokenAddress,\n    uint256 _tokenAmount,\n    uint32 _expirationTimestamp,\n    bytes calldata _collateralizableAllowanceSignature,\n    bytes calldata _tag\n  ) external nonReentrant returns (uint96) {\n    if (_tag.length > MAX_TAG_BYTES) revert TagTooLong(MAX_TAG_BYTES, _tag.length);\n    if (_collateralizableAllowanceSignature.length > 0) {\n      int256 amountWithFee = Pricing.safeCastToInt256(\n        // NB: amount with fee is what is reserved.\n        Pricing.amountWithFee(_tokenAmount, collateralContract.getWithdrawalFeeBasisPoints())\n      );\n\n      _tryModifyCollateralizableAllowanceWithSignature(\n        collateralContract,\n        _tokenAddress,\n        amountWithFee,\n        _collateralizableAllowanceSignature\n      );\n    }\n\n    return _createStaticLOC(msg.sender, _beneficiary, _tokenAddress, _tokenAmount, _expirationTimestamp, _tag);\n  }\n\n  /**\n   * @notice Redeems the referenced LOC, transferring the LOC's value to the specified destination address.\n   * Note: this can only be called by the beneficiary or with valid beneficiary authorization.\n   * @dev If `_creditedAmountToRedeem` is the full credited amount, any collateral that was not used in redemption\n   * will be canceled to the `ICollateral` contract. If `_creditedAmountToRedeem` is not the full amount, this LOC\n   * will remain intact with the remaining credited amount and collateral.\n   * @param _locId The ID of the loc to redeem on behalf of the beneficiary.\n   * @param _creditedAmountToRedeem The amount to redeem.\n   * @param _destinationAddress The address to which redeemed assets will be sent.\n   * @param _iLiquidatorToUse (optional) The ILiquidator to use for liquidation if required and not liquidating\n   * directly from the assets of `msg.sender`.\n   * @param _oraclePriceUpdate (optional) The oracle price update to be used if liquidation is required.\n   * @param _beneficiaryAuthorization (optional) The signed authorization of the beneficiary to cancel the LOC.\n   */\n  function redeemLOC(\n    uint96 _locId,\n    uint256 _creditedAmountToRedeem,\n    address _destinationAddress,\n    address _iLiquidatorToUse,\n    bytes calldata _oraclePriceUpdate,\n    bytes memory _beneficiaryAuthorization,\n    bytes calldata _liquidatorParams\n  ) external payable refundExcess nonReentrant {\n    LOC memory loc = locs[_locId];\n\n    if (msg.sender != loc.beneficiary) {\n      _validateRedeemAuth(\n        _locId,\n        _creditedAmountToRedeem,\n        loc.creditedTokenAmount,\n        _destinationAddress,\n        loc.beneficiary,\n        _beneficiaryAuthorization\n      );\n    }\n\n    _redeemLOC(\n      _locId,\n      loc,\n      _creditedAmountToRedeem,\n      _destinationAddress,\n      _iLiquidatorToUse,\n      _oraclePriceUpdate,\n      _liquidatorParams\n    );\n  }\n\n  /**\n   * @notice Cancels the referenced LOC, releasing any reserved collateral, returning any converted amount to the\n   * creator, and purging the LOC from storage.\n   * Note: this may only be called by the LOC beneficiary or with valid beneficiary authorization unless the LOC has\n   * expired.\n   * @param _locId The ID of the LOC to cancel.\n   * @param _beneficiaryAuthorization (optional) The signed authorization of the beneficiary to cancel the LOC.\n   */\n  function cancelLOC(uint96 _locId, bytes memory _beneficiaryAuthorization) external nonReentrant {\n    LOC memory loc = locs[_locId];\n    if (msg.sender != loc.beneficiary && loc.expirationTimestamp > block.timestamp) {\n      _validateCancelAuth(_locId, loc.beneficiary, _beneficiaryAuthorization);\n    }\n\n    _cancelLOC(_locId, loc);\n  }\n\n  /**\n   * @notice Extends the referenced LOC so that its expire time is increased to `newExpirationTimestamp`.\n   * @param _locId The ID of the LOC to extend.\n   * @param _newExpirationTimestamp The new expiration time of the LOC. Must be greater than the existing expiration time.\n   */\n  function extendLOC(uint96 _locId, uint32 _newExpirationTimestamp) external {\n    LOC memory loc = locs[_locId];\n    if (loc.creditedTokenAmount == 0) revert LOCNotFound(_locId);\n    if (msg.sender != loc.creator) revert AddressUnauthorizedForLOC(msg.sender, _locId);\n\n    uint32 oldExpirationTimestamp = loc.expirationTimestamp;\n    if (_newExpirationTimestamp <= oldExpirationTimestamp)\n      revert InvalidLOCExtensionTimestamp(_locId, _newExpirationTimestamp);\n    if (oldExpirationTimestamp <= block.timestamp) revert LOCExpired(_locId, oldExpirationTimestamp);\n    if (_newExpirationTimestamp - block.timestamp > maxLocDurationSeconds)\n      revert MaxLOCDurationExceeded(maxLocDurationSeconds, _newExpirationTimestamp);\n\n    // NB: pre-V3 this reverted if the pair's liquidator incentive had risen since LOC creation\n    // (the incentive was locked in per LOC). Collateral factors are global as of V3 — the current\n    // config always applies — so extension no longer changes the LOC's liquidation economics.\n    locs[_locId].expirationTimestamp = _newExpirationTimestamp;\n\n    emit LOCExtended(_locId, oldExpirationTimestamp, _newExpirationTimestamp);\n  }\n\n  /**\n   * @notice Adds/removes collateral to/from the specified LOC.\n   * @dev Note: collateral may only be removed from a LOC if the resulting collateral factor is at most the creation\n   * collateral factor for the asset pair (i.e. a new LOC could be created with the resulting collateral amount).\n   * @dev Only the creator may invoke this operation, as it is their collateral.\n   * @param _locId The ID of the LOC for which collateral should be modified.\n   * @param _byAmount The signed amount by which the collateral should be modified (add if positive, remove if negative).\n   * @param _oraclePriceUpdate (optional) The oracle price update to use if removing collateral to make sure the\n   * resulting amount of collateral is sufficient.\n   * @param _collateralizableAllowanceSignature (optional) The signature to allow this collateralizable to reserve the\n   * specified additional amount of the calling account's collateral within the `ICollateral` contract.\n   */\n  function modifyLOCCollateral(\n    uint96 _locId,\n    int256 _byAmount,\n    bytes calldata _oraclePriceUpdate,\n    bytes calldata _collateralizableAllowanceSignature\n  ) external payable refundExcess nonReentrant {\n    LOC memory loc = locs[_locId];\n\n    if (_byAmount == 0) revert NoOp();\n    if (loc.creditedTokenAmount == 0) revert LOCNotFound(_locId);\n    if (msg.sender != loc.creator) revert AddressUnauthorizedForLOC(msg.sender, _locId);\n    if (loc.collateralTokenAddress == loc.creditedTokenAddress) revert LOCAlreadyConverted(_locId);\n    if (loc.expirationTimestamp <= block.timestamp) revert LOCExpired(_locId, loc.expirationTimestamp);\n\n    if (_collateralizableAllowanceSignature.length > 0) {\n      // NB: this does not forbid decreasing the allowance if releasing collateral. That shouldn't happen often but is a legitimate use case.\n      _tryModifyCollateralizableAllowanceWithSignature(\n        loc.collateralContract,\n        loc.collateralTokenAddress,\n        _byAmount,\n        _collateralizableAllowanceSignature\n      );\n    }\n\n    // Update underlying collateral.\n    (uint256 newCollateralAmount, uint256 newClaimableAmount) = loc.collateralContract.modifyCollateralReservation(\n      loc.collateralId,\n      _byAmount\n    );\n\n    if (_byAmount <= 0) {\n      if (uint256(-_byAmount) >= loc.collateralTokenAmount)\n        revert InsufficientCollateral(uint256(_byAmount), loc.collateralTokenAmount);\n\n      uint256 requiredCollateralFactorBasisPoints = collateralToCreditedToCollateralFactors[loc.collateralTokenAddress][\n        loc.creditedTokenAddress\n      ].creationCollateralFactorBasisPoints;\n      if (requiredCollateralFactorBasisPoints == 0)\n        revert AssetPairUnauthorized(loc.collateralTokenAddress, loc.creditedTokenAddress);\n\n      Pricing.OraclePrice memory price;\n      if (_oraclePriceUpdate.length > 0) {\n        price = priceOracle.updatePrice{value: msg.value}(\n          loc.collateralTokenAddress,\n          loc.creditedTokenAddress,\n          _oraclePriceUpdate\n        );\n      } else {\n        price = priceOracle.getPrice(loc.collateralTokenAddress, loc.creditedTokenAddress);\n      }\n      _validatePricePublishTime(uint32(price.publishTime));\n\n      uint16 cfBasisPoints = Pricing.collateralFactorInBasisPoints(newCollateralAmount, loc.creditedTokenAmount, price);\n      if (cfBasisPoints > requiredCollateralFactorBasisPoints)\n        revert InsufficientCollateral(requiredCollateralFactorBasisPoints, cfBasisPoints);\n    }\n\n    // Update storage.\n    locs[_locId].collateralTokenAmount = newCollateralAmount;\n    locs[_locId].claimableCollateral = newClaimableAmount;\n\n    emit LOCCollateralModified(_locId, loc.collateralTokenAmount, newCollateralAmount, newClaimableAmount);\n  }\n\n  /*****************\n   * ILiquidatable *\n   *****************/\n\n  /**\n   * @inheritdoc ILiquidatable\n   * @dev As of V3, only unhealthy LOCs may be converted (see the LOCHealthy revert in\n   * _liquidateLOCCollateral); healthy LOCs are liquidated exclusively through redeemLOC. The\n   * _creatorAuthorization parameter is deprecated and ignored — creators no longer have\n   * voluntary-conversion rights, so there is nothing to authorize. It remains in the signature so\n   * the ILiquidatable ABI existing liquidation infrastructure depends on is unchanged.\n   */\n  function convertLOC(\n    uint96 _locId,\n    address _liquidatorToUse,\n    bytes calldata _oraclePriceUpdate,\n    bytes calldata /* _creatorAuthorization (deprecated, ignored) */,\n    bytes calldata _liquidatorParams\n  ) external payable refundExcess nonReentrant {\n    LOC memory loc = locs[_locId];\n\n    // NB: _liquidateLOCCollateral trusts its callers to validate; mirror _redeemLOC's checks.\n    if (loc.creditedTokenAmount == 0) revert LOCNotFound(_locId);\n    if (loc.collateralTokenAddress == loc.creditedTokenAddress) revert LOCAlreadyConverted(_locId);\n    if (loc.expirationTimestamp <= block.timestamp) revert LOCExpired(_locId, loc.expirationTimestamp);\n\n    _liquidateLOCCollateral(\n      _locId,\n      loc,\n      loc.creditedTokenAmount,\n      _liquidatorToUse,\n      _liquidatorParams,\n      _oraclePriceUpdate,\n      address(0)\n    );\n  }\n\n  /************************\n   * Governance Functions *\n   ************************/\n\n  /**\n   * @notice Upserts the supported `AssetPairCollateralFactors`, modifying if present, adding new otherwise.\n   * Note: setting the creation collateral factor to 0 effectively disables future use of an asset pair.\n   * @param _assetPairCollateralFactors The asset pair collateral factors to update\n   */\n  function upsertCollateralFactors(\n    AssetPairCollateralFactor[] calldata _assetPairCollateralFactors\n  ) external onlyOwner {\n    _upsertCollateralFactorsAsOwner(_assetPairCollateralFactors);\n  }\n\n  /**\n   * @notice Updates the supported `CreditedTokens` and their limits.\n   * Note: setting max per LOC or global max to 0 effectively disables a CreditedToken.\n   * @param _creditedTokens The credited tokens to add/modify.\n   */\n  function upsertCreditedTokens(CreditedTokenConfig[] calldata _creditedTokens) external onlyOwner {\n    _upsertCreditedTokensAsOwner(_creditedTokens);\n  }\n\n  /**\n   * @notice Updates the maximum age of a valid oracle price update.\n   * @param _maxPriceUpdateSecondsAgo The new value.\n   */\n  function updateMaxPriceUpdateSecondsAgo(uint16 _maxPriceUpdateSecondsAgo) external onlyOwner {\n    if (_maxPriceUpdateSecondsAgo < 30 || _maxPriceUpdateSecondsAgo > 3600)\n      revert InvalidMaxPriceUpdateSecondsAgo(30, 3600, _maxPriceUpdateSecondsAgo);\n\n    emit MaxPriceUpdateSecondsAgoUpdated(maxPriceUpdateSecondsAgo, _maxPriceUpdateSecondsAgo);\n    maxPriceUpdateSecondsAgo = _maxPriceUpdateSecondsAgo;\n  }\n\n  /**\n   * @notice Updates the supported `maxLocDurationSeconds` field.\n   * @param _newMaxLocDurationSeconds The new value.\n   */\n  function updateMaxLocDurationSeconds(uint32 _newMaxLocDurationSeconds) external onlyOwner {\n    emit MaxLocDurationSecondsUpdated(maxLocDurationSeconds, _newMaxLocDurationSeconds);\n    maxLocDurationSeconds = _newMaxLocDurationSeconds;\n  }\n\n  /**\n   * @notice Upgrades the `ICollateral` contract used to back new LOCs.\n   * Note: Existing LOCs will continue to use the ICollateral contract that they were created with.\n   * @param _collateralContract The new ICollateral contract to use.\n   */\n  function upgradeCollateralContract(ICollateral _collateralContract) public onlyOwner {\n    emit CollateralAddressUpgraded(address(collateralContract), address(_collateralContract));\n    collateralContract = _collateralContract;\n\n    // NB: if the _collateralContract is an EOA, the transaction will revert without a reason.\n    try IERC165(address(_collateralContract)).supportsInterface(type(ICollateral).interfaceId) returns (\n      bool supported\n    ) {\n      if (!supported) revert InvalidUpgradeContract();\n    } catch (bytes memory /*lowLevelData*/) {\n      revert InvalidUpgradeContract();\n    }\n  }\n\n  /**\n   * @notice Upgrades the `priceOracle` to the provided price oracle.\n   * @param _newPriceOracle The new IPriceOracle contract to upgrade.\n   */\n  function upgradePriceOracle(IPriceOracle _newPriceOracle) public onlyOwner {\n    if (_newPriceOracle == priceOracle) revert NoOp();\n    if (_newPriceOracle == IPriceOracle(address(0))) revert InvalidZeroAddress();\n\n    // NB: if the _newPriceOracle is an EOA, the transaction will revert without a reason.\n    try IERC165(address(_newPriceOracle)).supportsInterface(type(IPriceOracle).interfaceId) returns (bool supported) {\n      if (!supported) revert InvalidUpgradeContract();\n    } catch (bytes memory /*lowLevelData*/) {\n      revert InvalidUpgradeContract();\n    }\n    emit PriceOracleUpgraded(priceOracle, _newPriceOracle);\n\n    priceOracle = _newPriceOracle;\n  }\n\n  /*********************\n   * Private Functions *\n   *********************/\n\n  /**\n   * @notice Creates a static LOC, which is just a LOC where the credited asset is the collateral asset (1:1).\n   * @param _creator The transaction sender and creator of the LOC.\n   * @param _beneficiary The beneficiary of the LOC.\n   * @param _tokenAddress The token address of the credited and collateral asset.\n   * @param _creditedAmount The face value of the LOC.\n   * @param _expirationTimestamp The expiration time of the LOC.\n   * @param _tag (optional) The optional tag to be used to associate this LOC with the off-chain purpose of this LOC.\n   * @return _locId The created LOC ID.\n   */\n  function _createStaticLOC(\n    address _creator,\n    address _beneficiary,\n    address _tokenAddress,\n    uint256 _creditedAmount,\n    uint32 _expirationTimestamp,\n    bytes calldata _tag\n  ) private returns (uint96 _locId) {\n    if (_expirationTimestamp <= block.timestamp) revert LOCExpired(0, _expirationTimestamp);\n    if (_expirationTimestamp - block.timestamp > maxLocDurationSeconds)\n      revert MaxLOCDurationExceeded(maxLocDurationSeconds, _expirationTimestamp);\n\n    ICollateral colContract = collateralContract;\n    uint256 totalAmountReserved;\n    uint96 collateralId;\n    {\n      // NB: reserveClaimableCollateral because we need to guarantee _creditedAmount is available for claim.\n      // See ICollateral.reserve* for more info on different reservation options.\n      (collateralId, totalAmountReserved) = colContract.reserveClaimableCollateral(\n        _creator,\n        _tokenAddress,\n        _creditedAmount\n      );\n\n      /*** Create LOC ***/\n      _locId = ++locNonce;\n      locs[_locId] = LOC(\n        collateralId,\n        _creator,\n        _beneficiary,\n        _expirationTimestamp,\n        0, // Not liquidatable\n        0, // Not liquidatable\n        colContract,\n        _tokenAddress,\n        totalAmountReserved,\n        _creditedAmount,\n        _tokenAddress,\n        _creditedAmount\n      );\n    }\n\n    emit LOCCreatedV3(\n      _creator,\n      _beneficiary,\n      _tag,\n      address(colContract),\n      _tokenAddress,\n      totalAmountReserved,\n      _creditedAmount,\n      _expirationTimestamp,\n      _tokenAddress,\n      _creditedAmount,\n      _locId,\n      collateralId\n    );\n  }\n\n  /**\n   * @notice Creates a LOC with the caller as the creator with the specified parameters.\n   * @param _creator The creator of the LOC (assumed to be validated by the caller of this function).\n   * @param _beneficiary The beneficiary of the LOC.\n   * @param _collateralTokenAddress The token address of the collateral token.\n   * @param _collateralTokenAmount The amount of collateral to be locked.\n   * @param _creditedTokenAddress The token address of the credited token.\n   * @param _creditedTokenAmount The face value amount of the LOC.\n   * @param _expirationTimestamp The expiration time of the LOC.\n   * @param _price (optional) The oracle price to use for LOC creation calculations.\n   * @param _tag (optional) The optional tag to be used to associate this LOC with the off-chain purpose of this LOC.\n   * @return The ID of the created LOC.\n   */\n  function _createDynamicLOC(\n    address _creator,\n    address _beneficiary,\n    address _collateralTokenAddress,\n    uint256 _collateralTokenAmount,\n    address _creditedTokenAddress,\n    uint256 _creditedTokenAmount,\n    uint32 _expirationTimestamp,\n    Pricing.OraclePrice memory _price,\n    bytes calldata _tag\n  ) private returns (uint96) {\n    if (_expirationTimestamp <= block.timestamp) revert LOCExpired(0, _expirationTimestamp);\n    if (_expirationTimestamp - block.timestamp > maxLocDurationSeconds)\n      revert MaxLOCDurationExceeded(maxLocDurationSeconds, _expirationTimestamp);\n\n    _validatePricePublishTime(uint32(_price.publishTime));\n    _validateAndUpdateCreditedTokenUsageForDynamicLOCCreation(_creditedTokenAddress, _creditedTokenAmount);\n    _validateLOCCreationCollateralFactor(\n      _collateralTokenAddress,\n      _collateralTokenAmount,\n      _creditedTokenAddress,\n      _creditedTokenAmount,\n      _price\n    );\n\n    (uint96 collateralId, uint256 claimableCollateral) = collateralContract.reserveCollateral(\n      _creator,\n      _collateralTokenAddress,\n      _collateralTokenAmount\n    );\n\n    return\n      _persistAndEmitNewLOCCreated(\n        collateralId,\n        _creator,\n        _beneficiary,\n        _collateralTokenAddress,\n        _collateralTokenAmount,\n        claimableCollateral,\n        _creditedTokenAddress,\n        _creditedTokenAmount,\n        _expirationTimestamp,\n        _tag\n      );\n  }\n\n  /**\n   * @notice Persists new LOC and emits LOCCreated event.\n   * This is called in shared LOC creation flows to maintain consistency.\n   * @param _collateralId The ID of the collateral reservation for the LOC to be persisted.\n   * @param _creator The creator of the LOC (assumed to be validated by the caller of this function).\n   * @param _beneficiary The beneficiary of the LOC.\n   * @param _collateralTokenAddress The token address of the collateral token.\n   * @param _collateralTokenAmount The amount of collateral to be locked.\n   * @param _claimableCollateral The amount of the CollateralReservation that is claimable.\n   * This will be less than _collateralTokenAmount due to collateral withdrawal fees.\n   * @param _creditedTokenAddress The token address of the credited token.\n   * @param _creditedTokenAmount The face value amount of the LOC.\n   * @param _expirationTimestamp The expiration time of the LOC.\n   * @param _tag (optional) The optional tag to be used to associate this LOC with the off-chain purpose of this LOC.\n   * @return _locId The ID of the newly created LOC.\n   */\n  function _persistAndEmitNewLOCCreated(\n    uint96 _collateralId,\n    address _creator,\n    address _beneficiary,\n    address _collateralTokenAddress,\n    uint256 _collateralTokenAmount,\n    uint256 _claimableCollateral,\n    address _creditedTokenAddress,\n    uint256 _creditedTokenAmount,\n    uint32 _expirationTimestamp,\n    bytes calldata _tag\n  ) private returns (uint96 _locId) {\n    /*** Create LOC ***/\n    _locId = ++locNonce;\n    locs[_locId] = LOC(\n      _collateralId,\n      _creator,\n      _beneficiary,\n      _expirationTimestamp,\n      0, // Deprecated per-LOC collateralFactorBasisPoints; factors are global as of V3.\n      0, // Deprecated per-LOC liquidatorIncentiveBasisPoints; factors are global as of V3.\n      collateralContract,\n      _collateralTokenAddress,\n      _collateralTokenAmount,\n      _claimableCollateral,\n      _creditedTokenAddress,\n      _creditedTokenAmount\n    );\n\n    emit LOCCreatedV3(\n      _creator,\n      _beneficiary,\n      _tag,\n      address(collateralContract),\n      _collateralTokenAddress,\n      _collateralTokenAmount,\n      _claimableCollateral,\n      _expirationTimestamp,\n      _creditedTokenAddress,\n      _creditedTokenAmount,\n      _locId,\n      _collateralId\n    );\n  }\n\n  /**\n   * @dev Helper function to mark the provided LOC as converted, updating its fields in storage.\n   * @param _locId The ID of the LOC in question.\n   * @param _initiatorAddress The address of the party that initiated conversion.\n   * @param _liquidatorAddress The address of the liquidator used to trade collateral asset for credited asset.\n   * @param _loc The LOC being converted.\n   * @param _liquidationContext The `LiquidationContext` calculated for the conversion in question.\n   */\n  function _markLOCConverted(\n    uint96 _locId,\n    address _initiatorAddress,\n    address _liquidatorAddress,\n    LOC memory _loc,\n    LiquidationContext memory _liquidationContext\n  ) private {\n    LOC storage storedLoc = locs[_locId];\n\n    storedLoc.collateralTokenAmount = _liquidationContext.creditedTokenAmountToReceive;\n    storedLoc.creditedTokenAmount = _liquidationContext.creditedTokenAmountToReceive;\n    storedLoc.collateralTokenAddress = _loc.creditedTokenAddress;\n    storedLoc.claimableCollateral = 0;\n    storedLoc.collateralFactorBasisPoints = 0;\n    storedLoc.liquidatorIncentiveBasisPoints = 0;\n    storedLoc.collateralId = 0;\n    storedLoc.collateralContract = ICollateral(address(0));\n\n    // Free up global credited token max headroom.\n    creditedTokens[_loc.creditedTokenAddress].globalAmountInDynamicUse -= _loc.creditedTokenAmount;\n\n    emit LOCConverted(\n      _locId,\n      _initiatorAddress,\n      _liquidatorAddress,\n      _liquidationContext.liquidationAmount,\n      _liquidationContext.liquidatorFeeAmount,\n      _liquidationContext.creditedTokenAmountToReceive\n    );\n  }\n\n  /**\n   * @dev Helper function to mark the provided LOC as partially liquidated, updating its fields in storage.\n   * @param _locId The ID of the LOC in question.\n   * @param _initiatorAddress The address of the party that initiated partial liquidation.\n   * @param _liquidatorAddress The address of the liquidator used to trade collateral asset for credited asset.\n   * @param _collateralUsed The amount of collateral that was used in this partial liquidation. Note: this is more\n   * than the claimable collateral that was used, which is _liquidationContext.collateralToClaimAndSendLiquidator.\n   * @param _loc The LOC being partially liquidated.\n   * @param _liquidationContext The `LiquidationContext` calculated for the liquidation in question.\n   */\n  function _markLOCPartiallyLiquidated(\n    uint96 _locId,\n    address _initiatorAddress,\n    address _liquidatorAddress,\n    uint256 _collateralUsed,\n    LOC memory _loc,\n    LiquidationContext memory _liquidationContext\n  ) private {\n    LOC storage storedLoc = locs[_locId];\n\n    storedLoc.collateralTokenAmount = _loc.collateralTokenAmount - _collateralUsed;\n\n    storedLoc.claimableCollateral = _loc.claimableCollateral - _liquidationContext.collateralToClaimAndSendLiquidator;\n    storedLoc.creditedTokenAmount = _loc.creditedTokenAmount - _liquidationContext.creditedTokenAmountToReceive;\n\n    // Free up global credited token max headroom.\n    creditedTokens[_loc.creditedTokenAddress].globalAmountInDynamicUse -= _liquidationContext\n      .creditedTokenAmountToReceive;\n\n    emit LOCPartiallyLiquidated(\n      _locId,\n      _initiatorAddress,\n      _liquidatorAddress,\n      _liquidationContext.liquidationAmount,\n      _liquidationContext.liquidatorFeeAmount,\n      _liquidationContext.creditedTokenAmountToReceive\n    );\n  }\n\n  /**\n   * @dev Liquidates LOC collateral required to receive _requiredCreditedAmount, as determined by the oracle price.\n   * Note: if the _requiredCreditedAmount is the full credited amount of the LOC, it will mark the LOC as converted.\n   * @dev Only reachable from _redeemLOC and convertLOC, which perform LOC and caller validation\n   * before calling (LOC exists, is not expired, and its collateral is not already the credited\n   * asset; redemption is beneficiary-authorized with a non-zero destination).\n   * @param _locId The ID of the LOC in question.\n   * @param _loc The LOC in question, as loaded by the caller.\n   * @param _requiredCreditedAmount The credited amount required as a result of liquidation.\n   * @param _iLiquidatorToUse The liquidator to use to swap collateral for credited asset.\n   * Note: this should be the zero address if the caller will swap with this contract directly.\n   * @param _liquidatorParams Parameters, if any, to be parsed and used by the ILiquidator implementation contract.\n   * @param _oraclePriceUpdate The oracle price update bytes necessary to publish and read an updated oracle price.\n   * @param _redeemDestinationAddress The address to which the redeemed credited token will be sent,\n   * or the zero address when called from convertLOC (conversion, not redemption).\n   * @return _collateralUsed The amount of the reserved collateral that was used in conversion.\n   * @return _claimableCollateralUsed The amount of the claimable collateral that was used in conversion.\n   */\n  function _liquidateLOCCollateral(\n    uint96 _locId,\n    LOC memory _loc,\n    uint256 _requiredCreditedAmount,\n    address _iLiquidatorToUse,\n    bytes calldata _liquidatorParams,\n    bytes calldata _oraclePriceUpdate,\n    address _redeemDestinationAddress\n  ) private returns (uint256 _collateralUsed, uint256 _claimableCollateralUsed) {\n    address collateralTokenAddress = _loc.collateralTokenAddress;\n    address creditedTokenAddress = _loc.creditedTokenAddress;\n    bool partialRedeem = _requiredCreditedAmount != _loc.creditedTokenAmount;\n\n    /*** Calculate Liquidation Details ***/\n    LiquidationContext memory liquidationContext = _calculateLiquidationContext(\n      _loc,\n      _requiredCreditedAmount,\n      _oraclePriceUpdate\n    );\n    _claimableCollateralUsed = liquidationContext.collateralToClaimAndSendLiquidator;\n\n    // Healthy LOCs may only be liquidated to serve a redemption. NB: unlike pre-V3, this applies\n    // to the creator too — voluntary conversion of a healthy LOC is no longer possible.\n    if (!liquidationContext.locUnhealthy && _redeemDestinationAddress == address(0)) revert LOCHealthy(_locId);\n\n    if (\n      liquidationContext.collateralToClaimAndSendLiquidator == 0 || liquidationContext.creditedTokenAmountToReceive == 0\n    ) {\n      revert LiquidationAmountTooSmall(\n        liquidationContext.collateralToClaimAndSendLiquidator,\n        liquidationContext.creditedTokenAmountToReceive\n      );\n    }\n\n    address liquidatorAddress;\n    {\n      // NB: need to pop collateralRemaining off of the stack in this closure to avoid \"Stack too deep\" error after.\n      if (_iLiquidatorToUse != address(0)) {\n        liquidatorAddress = _iLiquidatorToUse;\n\n        // Claim collateral to this contract so liquidator may retrieve it from here in liquidate(...) call below.\n        {\n          (uint256 collateralRemaining, ) = _loc.collateralContract.claimCollateral(\n            _loc.collateralId,\n            _claimableCollateralUsed,\n            address(this),\n            !partialRedeem\n          );\n          _collateralUsed = _loc.collateralTokenAmount - collateralRemaining;\n        }\n\n        /*** Approve liquidator to withdraw the collateral from this contract ***/\n        // NB: we do not verify that the liquidator actually transferred this amount in order to save gas.\n        IERC20(collateralTokenAddress).forceApprove(_iLiquidatorToUse, _claimableCollateralUsed);\n\n        // NB: utilizing scope to free stack space and prevent \"Stack too deep\" compile error.\n        {\n          /*** Liquidate through ILiquidator interface ***/\n          uint256 creditedBalanceBefore = IERC20(creditedTokenAddress).balanceOf(address(this));\n\n          /*** Liquidate ***/\n          ILiquidator(_iLiquidatorToUse).liquidate(\n            msg.sender,\n            collateralTokenAddress,\n            _claimableCollateralUsed,\n            creditedTokenAddress,\n            liquidationContext.creditedTokenAmountToReceive,\n            _liquidatorParams\n          );\n\n          /*** Verify the received amount is exactly the expected amount ***/\n          uint256 receivedAmount = IERC20(creditedTokenAddress).balanceOf(address(this)) - creditedBalanceBefore;\n          if (receivedAmount != liquidationContext.creditedTokenAmountToReceive)\n            revert ConversionFundsReceivedMismatch(liquidationContext.creditedTokenAmountToReceive, receivedAmount);\n        }\n      } else {\n        liquidatorAddress = msg.sender;\n\n        // If the redeem recipient is the sender, we don't want to claim from them just to send to them. Note matching logic in _redeemLOC(...).\n        if (_redeemDestinationAddress != msg.sender) {\n          /*** Claim credited asset from msg.sender ***/\n          IERC20(creditedTokenAddress).safeTransferFrom(\n            msg.sender,\n            address(this),\n            liquidationContext.creditedTokenAmountToReceive\n          );\n        }\n        {\n          /*** Claim collateral from vault and disburse directly to liquidator (original caller) ***/\n          (uint256 collateralRemaining, ) = _loc.collateralContract.claimCollateral(\n            _loc.collateralId,\n            _claimableCollateralUsed,\n            msg.sender,\n            !partialRedeem\n          );\n\n          _collateralUsed = _loc.collateralTokenAmount - collateralRemaining;\n        }\n      }\n    }\n\n    if (partialRedeem) {\n      _markLOCPartiallyLiquidated(_locId, msg.sender, liquidatorAddress, _collateralUsed, _loc, liquidationContext);\n    } else {\n      _markLOCConverted(_locId, msg.sender, liquidatorAddress, _loc, liquidationContext);\n    }\n  }\n\n  /**\n   * Redeems the LOC in question, ignoring authorization checks with the assumption that they are handled by the caller.\n   * @dev If `_creditedAmountToRedeem` is the full credited amount, any collateral that was not used in redemption\n   * will be canceled to the `ICollateral` contract. If `_creditedAmountToRedeem` is not the full amount, this LOC\n   * will remain intact with the remaining credited amount and collateral.\n   * @param _locId The ID of the loc to redeem on behalf of the beneficiary.\n   * @param _loc The LOC to redeem on behalf of the beneficiary.\n   * @param _creditedTokenAmountToRedeem The amount of the credited token to redeem (may not be full LOC value).\n   * @param _destinationAddress The address to which redeemed assets will be sent.\n   * @param _iLiquidatorToUse (optional) The ILiquidator to use for liquidation if required and not liquidating\n   * directly from the assets of `msg.sender`.\n   * @param _oraclePriceUpdate (optional) The oracle price update to be used if liquidation is required.\n   * @param _liquidatorParams Parameters, if any, to be parsed and used by the ILiquidator implementation contract.\n   */\n  function _redeemLOC(\n    uint96 _locId,\n    LOC memory _loc,\n    uint256 _creditedTokenAmountToRedeem,\n    address _destinationAddress,\n    address _iLiquidatorToUse,\n    bytes calldata _oraclePriceUpdate,\n    bytes calldata _liquidatorParams\n  ) private {\n    if (_loc.creditedTokenAmount == 0) revert LOCNotFound(_locId);\n    if (_loc.expirationTimestamp <= block.timestamp) revert LOCExpired(_locId, _loc.expirationTimestamp);\n    if (_creditedTokenAmountToRedeem == 0 || _creditedTokenAmountToRedeem > _loc.creditedTokenAmount)\n      revert InvalidRedeemAmount(_creditedTokenAmountToRedeem, _loc.creditedTokenAmount);\n    if (_destinationAddress == address(0)) revert InvalidZeroAddress();\n\n    bool isPartialRedeem = _creditedTokenAmountToRedeem != _loc.creditedTokenAmount;\n\n    uint256 collateralUsed = 0;\n    uint256 claimableCollateralUsed = 0;\n\n    uint256 redeemedAmount = _creditedTokenAmountToRedeem;\n\n    /**\n        Steps:\n        1. Liquidate reserved collateral for credited asset if necessary.\n        2. Claim collateral if it is the credited asset and has not yet been claimed.\n        3. Transfer credited asset to _destinationAddress\n        */\n\n    // Liquidate collateral for credited asset if necessary. Note: This claims collateral.\n    if (_loc.collateralTokenAddress != _loc.creditedTokenAddress) {\n      (collateralUsed, claimableCollateralUsed) = _liquidateLOCCollateral(\n        _locId,\n        _loc,\n        _creditedTokenAmountToRedeem,\n        _iLiquidatorToUse,\n        _liquidatorParams,\n        _oraclePriceUpdate,\n        _destinationAddress\n      );\n    }\n\n    if (_loc.collateralId != 0 && _loc.collateralTokenAddress == _loc.creditedTokenAddress) {\n      // This means that the reserved collateral is in the credited asset.\n      // Claim assets directly to _destinationAddress.\n      uint256 collateralRemaining;\n      uint256 claimableCollateralRemaining;\n      // Claim directly to _destinationAddress\n      (collateralRemaining, claimableCollateralRemaining) = _loc.collateralContract.claimCollateral(\n        _loc.collateralId,\n        _creditedTokenAmountToRedeem, // NB: Credited is collateral token\n        _destinationAddress,\n        !isPartialRedeem\n      );\n      collateralUsed = _loc.collateralTokenAmount - collateralRemaining;\n      claimableCollateralUsed = _creditedTokenAmountToRedeem;\n\n      if (isPartialRedeem) {\n        // NB: Only update state for partial redeem because LOC storage will be deleted below for full redeem.\n        LOC storage storageLOC = locs[_locId];\n        storageLOC.collateralTokenAmount = collateralRemaining;\n        storageLOC.claimableCollateral = claimableCollateralRemaining;\n        storageLOC.creditedTokenAmount = claimableCollateralRemaining;\n      }\n    } else {\n      uint256 transferAmount;\n      // If we reach this code, we have converted necessary assets but not transferred them. Transfer assets.\n      if (isPartialRedeem) {\n        // NB: This line banks on the fact that we cannot partially redeem insolvent LOCs.\n        transferAmount = _creditedTokenAmountToRedeem;\n\n        if (claimableCollateralUsed == 0) {\n          // We partially redeemed a LOC that was already converted. Update the LOC state.\n          LOC storage storageLOC = locs[_locId];\n          // NB: This amount banks on the fact that we cannot partially redeem insolvent LOCs.\n          storageLOC.creditedTokenAmount = _loc.creditedTokenAmount - _creditedTokenAmountToRedeem;\n          collateralUsed = _creditedTokenAmountToRedeem;\n          storageLOC.collateralTokenAmount = _loc.collateralTokenAmount - collateralUsed;\n        }\n      } else {\n        // NB: This should be _creditedTokenAmountToRedeem, but use locs[_locId].creditedTokenAmount in case LOC is insolvent.\n        redeemedAmount = locs[_locId].creditedTokenAmount;\n        transferAmount = redeemedAmount;\n      }\n\n      // Only do transfer if the destination address is not acting as the direct liquidator.\n      // Otherwise we would claim the credited token from them to then transfer it to them.\n      if (_loc.collateralId == 0 || _iLiquidatorToUse != address(0) || _destinationAddress != msg.sender) {\n        IERC20(_loc.creditedTokenAddress).safeTransfer(_destinationAddress, transferAmount);\n      }\n    }\n\n    if (!isPartialRedeem) {\n      delete locs[_locId];\n    }\n\n    emit LOCRedeemed(_locId, _destinationAddress, redeemedAmount, collateralUsed, claimableCollateralUsed);\n  }\n\n  /**\n   * @dev Private helper function to cancel the LOC in question.\n   * Note: Assumes that caller validation has been done, but no other validation.\n   * @param _locId The ID of the LOC in question.\n   * @param _loc The LOC object.\n   */\n  function _cancelLOC(uint96 _locId, LOC memory _loc) private {\n    if (_loc.creditedTokenAmount == 0) revert LOCNotFound(_locId);\n\n    delete locs[_locId];\n\n    if (_loc.collateralId == 0) {\n      // NB: this means the LOC was converted through liquidation and the collateral has been seized by this contract.\n      // When converted, the collateral amount and token are updated to be the same as the credited amount and token.\n      IERC20(_loc.collateralTokenAddress).safeTransfer(_loc.creator, _loc.collateralTokenAmount);\n    } else {\n      _loc.collateralContract.releaseAllCollateral(_loc.collateralId);\n    }\n\n    emit LOCCanceled(_locId);\n\n    address creditedTokenAddress = _loc.creditedTokenAddress;\n    if (creditedTokenAddress != _loc.collateralTokenAddress) {\n      creditedTokens[creditedTokenAddress].globalAmountInDynamicUse -= _loc.creditedTokenAmount;\n    }\n  }\n\n  /**\n   * @dev attempts to modify the collateralizable allowance of the msg.sender with the provided signature without\n   * reverting if that call fails.\n   * @param _collateralContract The ICollateral contract to call.\n   * @param _collateralTokenAddress The collateral token for which the allowance is being modified.\n   * @param _collateralTokenAmount The signed amount by which the allowance should be modified (positive for increase, negative for decrease).\n   * @param _collateralizableAllowanceSignature The signature of the msg.sender approving this allowance adjustment.\n   */\n  function _tryModifyCollateralizableAllowanceWithSignature(\n    ICollateral _collateralContract,\n    address _collateralTokenAddress,\n    int256 _collateralTokenAmount,\n    bytes calldata _collateralizableAllowanceSignature\n  ) private {\n    try\n      _collateralContract.modifyCollateralizableTokenAllowanceWithSignature(\n        msg.sender,\n        address(this),\n        _collateralTokenAddress,\n        _collateralTokenAmount,\n        _collateralizableAllowanceSignature\n      )\n    {} catch (bytes memory reason) {\n      if (bytes4(reason) != ICollateral.InvalidSignature.selector) {\n        // Revert with the original error message\n        assembly (\"memory-safe\") {\n          revert(add(reason, 0x20), mload(reason))\n        }\n      }\n      // If it reverts for signature reasons, we do not want to let the transaction revert [yet].\n    }\n  }\n\n  /**\n   * @dev Helper function to validate the provided cancel authorization.\n   * Note: this reverts if the auth is invalid.\n   * @param _locId The ID of the LOC of the cancel authorization.\n   * @param _beneficiary The beneficiary of the LOC. The signature must match this address.\n   * @param _signature The signature that is being validated.\n   */\n  function _validateCancelAuth(uint96 _locId, address _beneficiary, bytes memory _signature) private {\n    bytes32 hash = _hashTypedDataV4(\n      keccak256(abi.encode(CANCEL_TYPEHASH, _locId, _useNonce(_beneficiary, CANCEL_TYPEHASH)))\n    );\n    if (!SignatureChecker.isValidSignatureNow(_beneficiary, hash, _signature)) {\n      revert InvalidSignature(_beneficiary);\n    }\n  }\n\n  /**\n   * @dev Helper function to validate the provided redeem authorization.\n   * Note: this reverts if the auth is invalid.\n   * @param _locId The ID of the LOC of the authorization.\n   * @param _redeemAmount The amount that is authorized for redemption.\n   * @param _totalCreditedAmount The current credited amount of the LOC. This prevents a redeem auth from being valid if the LOC has changed.\n   * @param _destination The destination address to which the redeemed tokens must be sent.\n   * @param _beneficiary The beneficiary of the LOC. The signature must match this address.\n   * @param _signature The signature that is being validated.\n   */\n  function _validateRedeemAuth(\n    uint96 _locId,\n    uint256 _redeemAmount,\n    uint256 _totalCreditedAmount,\n    address _destination,\n    address _beneficiary,\n    bytes memory _signature\n  ) private {\n    bytes32 hash = _hashTypedDataV4(\n      keccak256(\n        abi.encode(\n          REDEEM_TYPEHASH,\n          _locId,\n          _redeemAmount,\n          _totalCreditedAmount,\n          _destination,\n          _useNonce(_beneficiary, REDEEM_TYPEHASH)\n        )\n      )\n    );\n    if (!SignatureChecker.isValidSignatureNow(_beneficiary, hash, _signature)) {\n      revert InvalidSignature(_beneficiary);\n    }\n  }\n\n  /**\n   * @dev Helper function to verify the publish time of an oracle price.\n   * Note: Reverts on failure.\n   * @param _publishTime The publish time to validate.\n   */\n  function _validatePricePublishTime(uint32 _publishTime) private view {\n    if (_publishTime <= block.timestamp && block.timestamp - _publishTime > maxPriceUpdateSecondsAgo)\n      revert PriceUpdateStale(_publishTime, maxPriceUpdateSecondsAgo);\n  }\n\n  /**\n   * @dev Validates the LOC creation collateral factor for the potential LOC with the provided parameters.\n   * Note: Reverts on failure.\n   * @param _collateralTokenAddress The address of the collateral token of the potential LOC.\n   * @param _collateralTokenAmount The amount of the collateral token of the potential LOC.\n   * @param _creditedTokenAddress The address of the credited token of the potential LOC.\n   * @param _creditedTokenAmount The amount of the credited token of the potential LOC.\n   * @param _price The price to use to consider collateral factor validity.\n   */\n  function _validateLOCCreationCollateralFactor(\n    address _collateralTokenAddress,\n    uint256 _collateralTokenAmount,\n    address _creditedTokenAddress,\n    uint256 _creditedTokenAmount,\n    Pricing.OraclePrice memory _price\n  ) private view {\n    uint16 creationCollateralFactorBasisPoints = collateralToCreditedToCollateralFactors[_collateralTokenAddress][\n      _creditedTokenAddress\n    ].creationCollateralFactorBasisPoints;\n    if (creationCollateralFactorBasisPoints == 0)\n      revert AssetPairUnauthorized(_collateralTokenAddress, _creditedTokenAddress);\n\n    /*** Verify Collateral Factor ***/\n    uint16 currentCollateralFactorBasisPoints = Pricing.collateralFactorInBasisPoints(\n      _collateralTokenAmount,\n      _creditedTokenAmount,\n      _price\n    );\n\n    if (currentCollateralFactorBasisPoints > creationCollateralFactorBasisPoints)\n      revert InvalidCollateralFactor(creationCollateralFactorBasisPoints, currentCollateralFactorBasisPoints);\n  }\n\n  /**\n   * @dev Validates the provided CreditedToken for use in LOC creation, reverting if invalid.\n   * @param _creditedTokenAddress The address of the credited token.\n   * @param _creditedTokenAmount The amount of the credited token to be used for the creation of a LOC.\n   */\n  function _validateAndUpdateCreditedTokenUsageForDynamicLOCCreation(\n    address _creditedTokenAddress,\n    uint256 _creditedTokenAmount\n  ) private {\n    CreditedToken memory creditedToken = creditedTokens[_creditedTokenAddress];\n    if (_creditedTokenAmount > creditedToken.maxPerDynamicLOC)\n      revert LOCCreditedTokenMaxExceeded(creditedToken.maxPerDynamicLOC, _creditedTokenAmount);\n\n    if (_creditedTokenAmount < creditedToken.minPerDynamicLOC)\n      revert LOCCreditedTokenUnderMinimum(creditedToken.minPerDynamicLOC, _creditedTokenAmount);\n\n    uint256 newCreditedAmountInUse = creditedToken.globalAmountInDynamicUse + _creditedTokenAmount;\n    if (newCreditedAmountInUse > creditedToken.globalMaxInDynamicUse)\n      revert GlobalCreditedTokenMaxInUseExceeded(creditedToken.globalMaxInDynamicUse, newCreditedAmountInUse);\n\n    creditedTokens[_creditedTokenAddress].globalAmountInDynamicUse = newCreditedAmountInUse;\n  }\n\n  /**\n   * @dev Helper function to calculate and populate the `LiquidationContext` struct of a liquidation for the provided\n   * LOC with the provided oracle price.\n   * @param _loc The LOC for which liquidation context is being calculated.\n   * @param _requiredCreditedAmount The amount of the credited token needed as a result of liquidation.\n   * @param _oraclePriceUpdate The opaque oracle bytes to use to update the oracle price prior to calculation.\n   * @return The LiquidationContext struct with all the calculated fields necessary to carry out liquidation.\n   */\n  function _calculateLiquidationContext(\n    LOC memory _loc,\n    uint256 _requiredCreditedAmount,\n    bytes memory _oraclePriceUpdate\n  ) private returns (LiquidationContext memory) {\n    Pricing.OraclePrice memory price;\n    if (_oraclePriceUpdate.length > 0) {\n      price = priceOracle.updatePrice{value: msg.value}(\n        _loc.collateralTokenAddress,\n        _loc.creditedTokenAddress,\n        _oraclePriceUpdate\n      );\n    } else {\n      price = priceOracle.getPrice(_loc.collateralTokenAddress, _loc.creditedTokenAddress);\n    }\n    _validatePricePublishTime(uint32(price.publishTime));\n\n    // NB: collateral factors are global as of V3: read the pair's current config, never the values\n    // stored on the LOC (those are 0 for V3-created LOCs and stale snapshots for older ones).\n    CollateralFactor memory collateralFactor = collateralToCreditedToCollateralFactors[_loc.collateralTokenAddress][\n      _loc.creditedTokenAddress\n    ];\n\n    /*** Determine if this LOC is insolvent, and if so, adjust credited amount to receive. ***/\n    {\n      uint256 claimableCollateralInCreditedToken = Pricing.collateralAmountInCreditedToken(\n        _loc.claimableCollateral,\n        price\n      );\n\n      if (claimableCollateralInCreditedToken == 0) revert CollateralAmountInCreditedTokenZero();\n\n      uint256 maxCreditedTokenAmountToReceive = Pricing.amountBeforeFee(\n        claimableCollateralInCreditedToken,\n        collateralFactor.liquidatorIncentiveBasisPoints\n      );\n\n      if (maxCreditedTokenAmountToReceive < _loc.creditedTokenAmount) {\n        if (_requiredCreditedAmount != _loc.creditedTokenAmount) revert PartialRedeemInsolvent();\n        // This means that the LOC is insolvent, meaning the collateral is not enough to pay all fees and LOC face value.\n        // The liquidator and protocol will still get their full fees, but the beneficiary will not get LOC face value.\n        // This should never happen, but if somehow it does, the beneficiary should receive as much as possible.\n\n        // We know maxCreditedTokenAmountToReceive is all the credited we can receive using all claimable. Create a LiquidationContext representing this.\n        // NB: insolvent implies unhealthy, so the liquidator incentive (not the redeem buffer) applies.\n        return\n          _createLiquidationContextUsingAllClaimableCollateral(\n            _loc,\n            maxCreditedTokenAmountToReceive,\n            collateralFactor.liquidatorIncentiveBasisPoints\n          );\n      }\n    }\n\n    /*** LOC is not insolvent, so calculate liquidation amounts, leaving collateral to be received untouched. ***/\n\n    /**\n         1. get collateral value in credited\n         2. calculate fraction of that being used\n         3. multiply total collateral by that fraction to get collateral used\n         Note: liquidationAmount below combines 2 & 3 into 1 step\n         */\n    uint256 collateralAmountInCreditedToken = Pricing.collateralAmountInCreditedToken(\n      _loc.collateralTokenAmount,\n      price\n    );\n\n    // NB: Truncation is fine because we're checking >= for unhealthy below\n    bool locUnhealthy = (_loc.creditedTokenAmount * 10_000) / collateralAmountInCreditedToken >=\n      collateralFactor.collateralFactorBasisPoints;\n\n    // A healthy LOC is only ever liquidated to serve a redemption (_liquidateLOCCollateral reverts\n    // with LOCHealthy on the conversion path otherwise), so the redeem buffer is the fee taken from\n    // its collateral; unhealthy liquidations pay the full liquidator incentive.\n    uint16 feeBasisPoints = locUnhealthy\n      ? collateralFactor.liquidatorIncentiveBasisPoints\n      : collateralFactor.redeemBufferBasisPoints;\n\n    uint256 liquidationAmount = (_loc.collateralTokenAmount * _requiredCreditedAmount) /\n      collateralAmountInCreditedToken;\n\n    uint256 liquidatorFeeAmount = Pricing.percentageOf(liquidationAmount, feeBasisPoints);\n    uint256 collateralToClaim = liquidationAmount + liquidatorFeeAmount;\n    if (collateralToClaim > _loc.claimableCollateral) {\n      // We know this LOC is solvent, but it's unhealthy enough such that calculating collateral to claim from\n      // total collateral reserved loses enough precision such that it is greater than the claimable collateral.\n      // In this case, we'll use all claimable collateral and convert for the full amount.\n      return\n        _createLiquidationContextUsingAllClaimableCollateral(\n          _loc,\n          _requiredCreditedAmount,\n          collateralFactor.liquidatorIncentiveBasisPoints\n        );\n    }\n\n    return\n      LiquidationContext(\n        locUnhealthy,\n        _requiredCreditedAmount,\n        liquidationAmount,\n        liquidatorFeeAmount,\n        collateralToClaim\n      );\n  }\n\n  /**\n   * @notice Calculates creates a `LiquidationContext` using all of the provided `LOC's` claimable collateral\n   * specifying the provided amount of the credited asset as the expected amount to receive.\n   * Note: This `LiquidationContext` is hardcoded to be unhealthy. If all of a LOC's claimable collateral is necessary\n   * to trade into the credited asset, it cannot be healthy unless it is already converted.\n   * @param _loc The LOC for which the returned LiquidationContext is being created.\n   * @param _creditedToReceive The value that the `creditedTokenAmountToReceive` field should be set to.\n   * @param _liquidatorIncentiveBasisPoints The pair's current (global) liquidator incentive.\n   * @return The LiquidationContext.\n   */\n  function _createLiquidationContextUsingAllClaimableCollateral(\n    LOC memory _loc,\n    uint256 _creditedToReceive,\n    uint16 _liquidatorIncentiveBasisPoints\n  ) private pure returns (LiquidationContext memory) {\n    uint256 collateralToClaim = _loc.claimableCollateral;\n    uint256 collateralToTrade = Pricing.amountBeforeFee(collateralToClaim, _liquidatorIncentiveBasisPoints);\n    return\n      LiquidationContext(\n        true, // unhealthy because using all collateral means, at best, credited amount is 100% of the value of collateral, which is unhealthy in all cases.\n        _creditedToReceive,\n        collateralToTrade,\n        collateralToClaim - collateralToTrade,\n        collateralToClaim\n      );\n  }\n\n  /**\n   * @notice Upserts the supported `AssetPairCollateralFactors`, modifying if present, adding new otherwise.\n   * Note: setting the creation collateral factor to 0 effectively disables future use of an asset pair.\n   * @dev It is assumed that the caller of this function has verified that the msg.sender is the owner.\n   * @param _assetPairCollateralFactors The asset pair collateral factors to update\n   */\n  function _upsertCollateralFactorsAsOwner(AssetPairCollateralFactor[] memory _assetPairCollateralFactors) private {\n    for (uint256 i = 0; i < _assetPairCollateralFactors.length; ++i) {\n      AssetPairCollateralFactor memory apcf = _assetPairCollateralFactors[i];\n      CollateralFactor memory cf = apcf.collateralFactor;\n\n      uint16 liquidatorIncentiveBasisPoints = cf.liquidatorIncentiveBasisPoints;\n      if (liquidatorIncentiveBasisPoints > 10_000) revert InvalidBasisPointValue(liquidatorIncentiveBasisPoints);\n\n      uint16 creationCFBasisPoints = cf.creationCollateralFactorBasisPoints;\n      if (creationCFBasisPoints > 10_000) revert InvalidBasisPointValue(creationCFBasisPoints);\n\n      uint16 liquidationCFBasisPoints = cf.collateralFactorBasisPoints;\n      if (liquidationCFBasisPoints <= creationCFBasisPoints)\n        revert CollateralFactorOverlap(creationCFBasisPoints, liquidationCFBasisPoints);\n\n      uint16 maxLiquidatorIncentive = 10_000 - liquidationCFBasisPoints;\n      if (liquidatorIncentiveBasisPoints > maxLiquidatorIncentive)\n        revert LiquidatorIncentiveAboveMax(maxLiquidatorIncentive, liquidatorIncentiveBasisPoints);\n\n      // The redeem buffer is a fee taken from collateral just like the liquidator incentive, so it\n      // is bounded the same way.\n      uint16 redeemBufferBasisPoints = cf.redeemBufferBasisPoints;\n      if (redeemBufferBasisPoints > maxLiquidatorIncentive)\n        revert RedeemBufferAboveMax(maxLiquidatorIncentive, redeemBufferBasisPoints);\n\n      address collateralAddress = apcf.collateralTokenAddress;\n      address creditedAddress = apcf.creditedTokenAddress;\n\n      collateralToCreditedToCollateralFactors[collateralAddress][creditedAddress] = cf;\n\n      emit CollateralFactorUpdatedV2(\n        collateralAddress,\n        creditedAddress,\n        creationCFBasisPoints,\n        liquidationCFBasisPoints,\n        liquidatorIncentiveBasisPoints,\n        redeemBufferBasisPoints\n      );\n    }\n  }\n\n  /**\n   * @notice Updates the supported `CreditedTokens` and their limits.\n   * Note that setting global max in use to 0 effectively disables a CreditedToken.\n   * @dev It is assumed that the caller of this function has verified that the msg.sender is the owner.\n   * @param _creditedTokens The credited tokens to add/modify.\n   */\n  function _upsertCreditedTokensAsOwner(CreditedTokenConfig[] memory _creditedTokens) private {\n    for (uint256 i = 0; i < _creditedTokens.length; ++i) {\n      CreditedTokenConfig memory config = _creditedTokens[i];\n      uint256 minPerDynamicLOC = config.minPerDynamicLOC;\n      uint256 maxPerDynamicLOC = config.maxPerDynamicLOC;\n      address tokenAddress = config.tokenAddress;\n      uint256 globalMaxInDynamicUse = config.globalMaxInDynamicUse;\n\n      // NB: If disabling this credited token, should be able to zero out all state, else validate.\n      if (globalMaxInDynamicUse > 0) {\n        if (minPerDynamicLOC >= maxPerDynamicLOC) revert CreditedTokenMinMaxOverlap();\n        if (minPerDynamicLOC == 0) revert EnabledCreditedTokenMinPerDynamicLOCZero();\n      }\n\n      creditedTokens[tokenAddress].minPerDynamicLOC = minPerDynamicLOC;\n      creditedTokens[tokenAddress].maxPerDynamicLOC = maxPerDynamicLOC;\n      creditedTokens[tokenAddress].globalMaxInDynamicUse = globalMaxInDynamicUse;\n\n      emit CreditedTokenUpdated(tokenAddress, minPerDynamicLOC, maxPerDynamicLOC, globalMaxInDynamicUse);\n    }\n  }\n}\n","deployed_bytecode":"0x6080604052600436101561001257600080fd5b60003560e01c8063052e71a014613c3b5780630b24ec6814613b8e5780631008448314613afc5780632630c12f14613ad35780634e665cad14613a4a5780635d83d1f51461328257806362d751bd1461321a578063715018a61461319957806373fca6ea1461315e57806379ba50971461310b5780637e568658146130325780637f691199146129e157806384b0196e1461288557806384b9bc9f1461281b57806385a99879146127e0578063896797a6146127bb5780638da5cb5b146127855780639c9fb5fb14612590578063ac36c5d114612569578063c10fadb814611e23578063c1c7816e14611e06578063c6e1c7c914611ddd578063ce85428714611a4b578063d06d883414610cdf578063d723365114610c78578063dc9e0faa14610b4d578063e085a0f514610a80578063e30c397814610a4a578063eba8f9fd14610458578063f2fde38b146103d15763fd948dea1461017157600080fd5b346103cc5760403660031901126103cc5761018a6141a7565b60243563ffffffff91828216928383036103cc576001600160601b0393848316948560005260036020526040600020604051916101c6836141cc565b81549081168352602083019060601c8152610160600183015493600760018060a01b039485871660408401526060830196898160a01c16885261ffff90818160c01c16608086015260d01c1660a08401528560028201541660c08401528560038201541660e0840152600481015461010084015260058101546101208401528560068201541661014084015201549182910152156103b3575116330361038c57518216928382111561035d574284111561032e57506102869042906144e2565b9060005460701c1680911161030757506000838152600360205260409020600101805463ffffffff60a01b191660a084901b63ffffffff60a01b161790557fcbe79b0f0cfd0f9b04e2c1db6a49dd1ed5805d75196ca39b32f61d51aae6dca991906040805163ffffffff9283168152929091166020830152819081010390a2005b60405163cf113d3160e01b815263ffffffff91821660048201529216602483015250604490fd5b604051632cbdae0760e01b81526001600160601b0391909116600482015263ffffffff84166024820152604490fd5b604051632207376560e01b81526001600160601b0391909116600482015263ffffffff85166024820152604490fd5b604051636bcb678560e11b81523360048201526001600160601b0385166024820152604490fd5b60405163810fbf1f60e01b815260048101899052602490fd5b600080fd5b346103cc5760203660031901126103cc576103ea61410e565b6103f26147df565b6000805160206156b583398151915280546001600160a01b0319166001600160a01b03928316908117909155600080516020615615833981519152549091167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e22700600080a3005b60803660031901126103cc5761046c6141a7565b6001600160401b03906044358281116103cc5761048d90369060040161417a565b926064359081116103cc576104a690369060040161417a565b939047926104b26145f4565b6001600160601b03851660005260036020526040600020916007604051936104d9856141cc565b80546001600160601b038116865260601c602086015261ffff600182015460018060a01b038116604088015263ffffffff8160a01c166060880152818160c01c16608088015260d01c1660a086015260018060a01b0360028201541660c086015260018060a01b0360038201541660e08601526004810154610100860152600581015461012086015260018060a01b03600682015416610140860152015461016084015260243515610a395761016083015115610a185760208301516001600160a01b031633036109f15760e08301516101408401516001600160a01b039081169116146109d05763ffffffff60608401511696428811156109a35760009596975080610977575b5050604060018060a01b0360c08401511660446001600160601b03855116835197889384926360b05e7160e01b8452600484015260243560248401525af19283156106f857600094600094610940575b506000602435131561070d575b5050846106b27f7ff335afdf1474dc98fe86cdafdd3882799b79b4a48004d6e766e2615ef22765936101006001600160601b0394856106ce9a16600052600360205282600560406000208a600482015501550151956040519485941696846040919493926060820195825260208201520152565b0390a260016000805160206156758339815191525534906144e2565b8047116106d757005b600080806106e68194476144e2565b818115610704575b3390f1156106f857005b6040513d6000823e3d90fd5b506108fc6106ee565b602435600160ff1b1461092a5761010083015180602435600003101561090a575060e08301516001600160a01b0390811660009081526005602090815260408083206101408801519094168352929052205461ffff169182156108d657610772614537565b5081156108545760025460e085015161014086015160405163b37b327160e01b81529460609486946001600160a01b0390811694869485946107c094909390929181169116600486016145b5565b039134905af19081156106f85761ffff916107fe91600091610825575b505b6107f263ffffffff604083015116614d0c565b61016085015187614d98565b168181111561063e576044925060405191632c1f8ef160e21b835260048301526024820152fd5b610847915060603d60601161084d575b61083f8183614267565b810190614556565b896107dd565b503d610835565b505060025460e0830151610140840151604051635620c32d60e11b81526001600160a01b03928316600482015290821660248201529160609183916044918391600091165af19081156106f85761ffff916107fe916000916108b7575b506107df565b6108d0915060603d60601161084d5761083f8183614267565b896108b1565b60e0840151610140850151604051636a6fc48160e01b81526001600160a01b03928316600482015291166024820152604490fd5b60449060405190632c1f8ef160e21b825260243560048301526024820152fd5b634e487b7160e01b600052601160045260246000fd5b90935061096691945060403d604011610970575b61095e8183614267565b8101906145de565b9390939287610631565b503d610954565b60c084015160e085015161099c9391602435916001600160a01b03908116911661465a565b86806105e1565b604051632cbdae0760e01b81526001600160601b038816600482015263ffffffff89166024820152604490fd5b6040516332f1c16d60e11b81526001600160601b0387166004820152602490fd5b604051636bcb678560e11b81523360048201526001600160601b0387166024820152604490fd5b60405163810fbf1f60e01b81526001600160601b0387166004820152602490fd5b60405162219cf160e01b8152600490fd5b346103cc5760003660031901126103cc576000805160206156b5833981519152546040516001600160a01b039091168152602090f35b346103cc5760203660031901126103cc5761ffff6004358181168082036103cc57610aa96147df565b601e81108015610b42575b610b1b575060005460408051606083901c9490941661ffff90811685528316602085015290927fff2f93c7ba57f9142fd98325a32e23b17b30d1d203f406d7eb04a55b5c3957ac9190a161ffff60601b1990911660609190911b61ffff60601b1617600055005b606490604051906310279acb60e31b8252601e6004830152610e1060248301526044820152fd5b50610e108111610ab4565b346103cc5760203660031901126103cc57610b6661410e565b610b6e6147df565b6002546001600160a01b03918216918116828114610a39578215610c66576040516301ffc9a760e01b8152633cb4bea160e01b6004820152602081602481875afa60009181610c35575b50610bdb57610bc5614507565b506040516001621a684d60e01b03198152600490fd5b15610c205760407fed5e4ea1344427f93380f9247ae9da395842832daa39465284c4274ac799ac52918151908152846020820152a16001600160a01b03191617600255005b6040516001621a684d60e01b03198152600490fd5b610c5891925060203d602011610c5f575b610c508183614267565b8101906144ef565b9085610bb8565b503d610c46565b60405163f6b2911f60e01b8152600490fd5b346103cc5760403660031901126103cc576001600160a01b03610c9961410e565b166000527fa10463db4f5369b35ba0ad7c2c4710776052e62babfb578b03cd47bf9aa4f10060205260406000206024356000526020526020604060002054604051908152f35b60e03660031901126103cc57610cf36141a7565b610cfb614150565b90610d0461413a565b906084356001600160401b0381116103cc57610d2490369060040161417a565b939060a4356001600160401b0381116103cc57610d4590369060040161449f565b9060c4356001600160401b0381116103cc57610d6590369060040161417a565b9390914796610d726145f4565b6001600160601b0387166000526003602052604060002060405195610d96876141cc565b81546001600160601b0381168852606090811c602089015260018301546001600160a01b0380821660408b0181905260a083811c63ffffffff16948c019490945260c083811c61ffff90811660808e015260d085901c16948c019490945260028601548216938b01939093526003850154811660e08b015260048501546101008b015260058501546101208b01526006850154166101408a015260079093015461016089018190529190330361191c575b505050610160850151156118fb5763ffffffff606086015116428111156118cc57506024351580156118bc575b611897576001600160a01b03831615610c665761016085015160e086015161014087015160009b60243599938a14978d9690946001600160a01b03948516949293909216908185036111e7575b5050505050506001600160601b038551161515806111c8575b15611074575050610f24965060408260018060a01b0360c0860151168360006001600160601b0388511685519c8d9586948593637500616d60e01b85526024359060048601614800565b03925af19283156106f8576106ce977fa401b77f6ebc213dba24e20584bca0389bbd6391cf1498bbbf4feac74932f68a946001600160601b0394610fb993600093600093611045575b5083610100610f7d9201516144e2565b9160243593821561101d575b50505b610fd5575b60405194859460018060a01b0316981696846040919493926060820195825260208201520152565b0390a360016000805160206156758339815191525534906144e2565b8488166000526003602052611018604060002060076000918281558260018201558260028201558260038201558260048201558260058201558260068201550155565b610f91565b600790888c166000526003602052604060002090600482015582600582015501558b80610f89565b6101009450610f7d9193506110689060403d6040116109705761095e8183614267565b90945092905083610f6d565b936106ce989391929482156000146111785792610fb9926001600160601b03959287957fa401b77f6ebc213dba24e20584bca0389bbd6391cf1498bbbf4feac74932f68a9860243591861561112e575b898451161590811561111b575b508015611108575b6110e6575b505050610f8c565b6101409092015161110092906001600160a01b031661528e565b8b80866110de565b506001600160a01b0381163314156110d9565b6001600160a01b0316151590508f6110d1565b945050878b166000526003602052866040600020946111546024356101608601516144e2565b60078701556024359560046111706024356101008801516144e2565b9101556110c4565b9550927fa401b77f6ebc213dba24e20584bca0389bbd6391cf1498bbbf4feac74932f68a9492610fb9926001600160601b0395868a16600052600360205260076040600020015498868a916110c4565b5060e08501516101408601516001600160a01b03908116911614610eda565b6112019193959e5061120a92949697506000953691614468565b6024358a614e0d565b926080840151948451158061188f575b61186e57608085015180158015611862575b61184057506001600160a01b0387161561174757869361128260408b8d6001600160601b0360018060a01b0360c08301511691511660008c855196879586948593637500616d60e01b8552309160048601614800565b03925af180156106f8576112a491600091611727575b506101008d01516144e2565b9e886040516000808b6112f1846112e3602082019363095ea7b360e01b988986526024840160209093929193604081019460018060a01b031681520152565b03601f198101865285614267565b83519082895af1611300614507565b816116f8575b50806116ee575b156116a9575b50506040516370a0823160e01b815230600482015292602084602481895afa9384156106f857600094611675575b506020880151906001600160a01b038b163b156103cc5761139d60009360405195869485946335d090f360e11b865233600487015260248601528d60448601528a6064860152608485015260c060a485015260c4840191614594565b0381836001600160a01b038d165af180156106f85761165d575b50602060249293604051938480926370a0823160e01b82523060048301525afa801561165257839061161e575b6113ee92506144e2565b602084015190818103611600575050918991888d945b896114ff57906114b5600360407f103624043a1a382761fb8e47bd03d967b68fce58c92eabd1c990478babf49d8b95946001600160601b0389168152826020526114568282209a6101008701516144e2565b60048b015561146f61012086015160808a0151906144e2565b60058b015561016085015199600761148d60208b019c8d51906144e2565b9101558951610140909501516001600160a01b031681526004602052200180549092906144e2565b9055604083810151606094850151965182519182526020820197909752908101959095526001600160a01b03169333936001600160601b03169290a45b9790898080808080610ec1565b7f21640bc6d24e28d13a51d024a2c86f8dd94df3889ab646059044fad4cf4eded4929195506001600160601b038516815260036020526115bf6003604080842093610160602089019a8b51806004890155600788015560018060a01b0361014082015116966002868201916001600160601b0360a01b928a848254161790558560058201556001810163ffffffff60c01b1981541690556001600160601b03198154168155019081541690550151948152600460205220019182546144e2565b9055604083810151606094850151965182519182526020820197909752908101959095526001600160a01b03169333936001600160601b03169290a46114f2565b604492506040519163799fce1f60e11b835260048301526024820152fd5b506020823d60201161164a575b8161163860209383614267565b810103126103cc576113ee91516113e4565b3d915061162b565b6040513d85823e3d90fd5b6024925061166a9061421e565b6020600092506113b7565b9093506020813d6020116116a1575b8161169160209383614267565b810103126103cc57519238611341565b3d9150611684565b6116e16116e79260405190602082015260018060a01b038d16602482015260006044820152604481526116db816141e8565b866153ba565b846153ba565b3880611313565b50843b151561130d565b805180159250821561170d575b505038611306565b61172092506020809183010191016144ef565b3880611705565b611740915060403d6040116109705761095e8183614267565b5038611298565b50979b506117a39792339250906001600160a01b0387168390036117f1575b506040878d6001600160601b0360018060a01b0360c08301511691511660008451809d81958294637500616d60e01b84528c339160048601614800565b03925af19182156106f8576117c98b938e9a6000916117d1575b506101008b01516144e2565b9c8d94611404565b6117ea915060403d6040116109705761095e8183614267565b508f6117bd565b6020828101516040516323b872dd60e01b92810192909252336024830152306044830152606482015261183a9161183582608481015b03601f198101845283614267565b6153ba565b8c611766565b60449060208701516040519163c661aab760e01b835260048301526024820152fd5b5060208601511561122c565b604051639df5f2d760e01b81526001600160601b038d166004820152602490fd5b50600061121a565b60446101608601516040519063153360a160e01b825260243560048301526024820152fd5b5061016085015160243511610e74565b604051632cbdae0760e01b81526001600160601b038916600482015263ffffffff919091166024820152604490fd5b60405163810fbf1f60e01b81526001600160601b0388166004820152602490fd5b60018060a01b0383166000527fa10463db4f5369b35ba0ad7c2c4710776052e62babfb578b03cd47bf9aa4f1006020526040600020917f15cdf564776febe3f454b055f4b652fb3b48292468cdcbbc896f19f1f5bd7cad92836000526020526040600020908154916001830190556040519360208501526001600160601b038c1660408501526024356060850152608084015260018060a01b03871660a084015260c083015260c082528160e08101106001600160401b0360e084011117611a35576119f88260e0611a07940160405260208151910120615120565b6001600160a01b0384166151af565b15611a13578080610e47565b604051633615713d60e21b81526001600160a01b039091166004820152602490fd5b634e487b7160e01b600052604160045260246000fd5b346103cc5760403660031901126103cc57611a646141a7565b6024356001600160401b0381116103cc57611a8390369060040161449f565b90611a8c6145f4565b6001600160601b0381166000526003602052604060002060405192611ab0846141cc565b81546001600160601b0381168552606090811c602086015260018301546001600160a01b038082166040880181905260a083811c63ffffffff169489019490945260c083811c61ffff90811660808b015260d085901c169489019490945260028601548216938801939093526003850154811660e08801526004850154610100880152600585015461012088015260068501541661014087015260079093015461016086015233141580611dcb575b611d19575b505061016082015115611cf7576001600160601b0381166000526003602052611bbb604060002060076000918281558260018201558260028201558260038201558260048201558260058201558260068201550155565b81516001600160601b031680611c80575060e082015160208301516101008401516001600160601b039392611bfc926001600160a01b03908116911661528e565b167f1706888457c949ef810b6308f2cb8280ecb6d22a992c4b53cf15f25405020c10600080a261014081015160e08201516001600160a01b039182169291168203611c56575b600160008051602061567583398151915255005b6101600151906000526004602052611c7760036040600020019182546144e2565b90558080611c42565b60c0830151604051630834ef4b60e21b81526004810192909252602090829060249082906000906001600160a01b03165af180156106f857611ccc575b506001600160601b0390611bfc565b602090813d8311611cf0575b611ce28183614267565b810103126103cc5782611cbd565b503d611cd8565b60405163810fbf1f60e01b81526001600160601b039091166004820152602490fd5b611dc09060018060a01b0383166000527fa10463db4f5369b35ba0ad7c2c4710776052e62babfb578b03cd47bf9aa4f1006020526119f860406000207fb2c9dd2d7f83ecfa5c4f5d7679b1f3acfbb0d53f15dccf8b1524979826eaa6c79081600052602052604060002080549060018201905560405190602082019283526001600160601b0388166040830152606082015260608152611db8816141e8565b519020615120565b15611a135780611b64565b504263ffffffff8360a01c1611611b5f565b346103cc5760003660031901126103cc576001546040516001600160a01b039091168152602090f35b346103cc5760003660031901126103cc5760206040516102008152f35b6101203660031901126103cc57611e3861410e565b611e40614124565b90611e4961413a565b9163ffffffff60a4351660a435036103cc576001600160401b0360c4358181116103cc57611e7b90369060040161417a565b929060e4358381116103cc57611e9590369060040161417a565b949093610104359081116103cc57611eb190369060040161417a565b9290944796611ebe6145f4565b6001600160a01b03868116908b16146125575761020080861161253957508061250f575b5050611eec614537565b50811561249f5760025460405163b37b327160e01b81529260609284926001600160a01b03169183918291611f26918e8b600486016145b5565b039134905af19081156106f857600091612480575b505b4263ffffffff60a43516111561245957611f5f4263ffffffff60a435166144e2565b63ffffffff60005460701c1680911161242f5750611f8663ffffffff604083015116614d0c565b60018060a01b03871660005260046020526040600020604051611fa8816141e8565b8154815260018201549081602082015260036002840154936040830194855201549160608201928352806084351161240f57505180608435106123ef5750611ff4906084359051614624565b90518082116123d257506001600160a01b038881166000818152600460209081526040808320600301959095559287168152600583528381209181529152205461ffff169081156123a95761204e90608435604435614d98565b908061ffff8316116123855750506001546040805163a2e115e960e01b81523360048201526001600160a01b038581166024830152604480359083015290979288916064918391600091165af19081156106f857612304967f6b401e682129f6a19f416a78c37fd6d263bfc32f134438588451bfbbb7b9baed9360009160009461234e575b50600054610fb992916120ee6001600160601b038316614755565b6bffffffffffffffffffffffff199092166001600160601b03831617600055600154604051929c8d9390916001600160a01b031661212b826141cc565b6001600160601b038616825233602083015260018060a01b038816604083015263ffffffff60a43516606083015260006080830152600060a083015260c082015260018060a01b038a1660e08201526044356101008201528761012082015260018060a01b0382166101408201526084356101608201526001600160601b038416600052600360205260076101606040600020926001600160601b038151166001600160601b0319602083015160601b161784556001840161227760018060a01b03604084015116916001600160601b0360a01b928382541617815561223763ffffffff606086015116829081549063ffffffff60a01b9060a01b169063ffffffff60a01b1916179055565b6080840151815461ffff60c01b191660c09190911b61ffff60c01b1617815560a0840151815461ffff60d01b191660d09190911b61ffff60d01b16179055565b60c082015160028601805483166001600160a01b0392831617905560e08301516003870180548416918316919091179055610100830151600487015561012083015160058701556101408301516006870180549093169082161790915591015191909201556001546040519782169b339b98998a996084359660a43595929460443594929316918c61476d565b804711612320575b6020826001600160601b0360405191168152f35b6000808061232f8194476144e2565b818115612345575b3390f1156106f8578161230c565b506108fc612337565b612376919450610fb9925060403d60401161237e575b61236e8183614267565b81019061472e565b9390916120d3565b503d612364565b604051630ed2e35760e41b815261ffff918216600482015291166024820152604490fd5b604051636a6fc48160e01b81526001600160a01b03858116600483015289166024820152604490fd5b6044916040519163eee9771b60e01b835260048301526024820152fd5b60449060405190635ac8b2d960e01b825260048201526084356024820152fd5b6044906040519063cfb00aa360e01b825260048201526084356024820152fd5b60405163cf113d3160e01b815263ffffffff918216600482015260a4359091166024820152604490fd5b604051632cbdae0760e01b81526000600482015260a43563ffffffff166024820152604490fd5b612499915060603d60601161084d5761083f8183614267565b87611f3b565b5050600254604051635620c32d60e11b81526001600160a01b0384811660048301528881166024830152909160609183916044918391600091165af19081156106f8576000916124f0575b50611f3d565b612509915060603d60601161084d5761083f8183614267565b876124ea565b60015461253292906001600160a01b03168761252c604435614631565b9161465a565b8880611ee2565b604490866040519163b2560b7960e01b835260048301526024820152fd5b6040516314b7e24960e31b8152600490fd5b346103cc5760003660031901126103cc57602063ffffffff60005460701c16604051908152f35b346103cc5760203660031901126103cc576001600160601b036125b16141a7565b60006101606040516125c2816141cc565b8281528260208201528260408201528260608201528260808201528260a08201528260c08201528260e082015282610100820152826101208201528261014082015201521660005260036020526101806040600020610160604051612626816141cc565b60078354936001600160601b03851683528460601c602084015261ffff600182015460018060a01b038116604086015263ffffffff8160a01c166060860152818160c01c16608086015260d01c1660a084015260018060a01b0360028201541660c084015260018060a01b0360038201541660e08401526004810154610100840152600581015461012084015260018060a01b036006820154166101408401520154828201526001600160601b036040519316835260018060a01b03602082015116602084015260018060a01b03604082015116604084015263ffffffff606082015116606084015261ffff608082015116608084015261ffff60a08201511660a084015260018060a01b0360c08201511660c084015260018060a01b0360e08201511660e084015261010081015161010084015261012081015161012084015260018060a01b03610140820151166101408401520151610160820152f35b346103cc5760003660031901126103cc57600080516020615615833981519152546040516001600160a01b039091168152602090f35b346103cc5760003660031901126103cc57602061ffff60005460601c16604051908152f35b346103cc5760003660031901126103cc5760206040517f15cdf564776febe3f454b055f4b652fb3b48292468cdcbbc896f19f1f5bd7cad8152f35b346103cc5760203660031901126103cc576004356001600160401b038082116103cc57366023830112156103cc5781600401359081116103cc5736602460c08302840101116103cc576128839161287e916128746147df565b602436920161432d565b6149b1565b005b346103cc5760003660031901126103cc577fa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d1005415806129b8575b1561297b576128cc614ba9565b6128d4614c6f565b6040516020808201928284106001600160401b03851117611a355791602061292e8594612920979660405260008452604051978897600f60f81b895260e0858a015260e089019061440d565b90878203604089015261440d565b91466060870152306080870152600060a087015285830360c0870152519182815201929160005b82811061296457505050500390f35b835185528695509381019392810192600101612955565b60405162461bcd60e51b81526020600482015260156024820152741152540dcc4c8e88155b9a5b9a5d1a585b1a5e9959605a1b6044820152606490fd5b507fa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d10154156128bf565b346103cc5760e03660031901126103cc576129fa61410e565b612a02614124565b90612a0b614150565b9161ffff60643516606435036103cc5763ffffffff60843516608435036103cc5760a4356001600160401b0381116103cc57366023820112156103cc57612a5c90369060248160040135910161429f565b9260c4356001600160401b0381116103cc57366023820112156103cc57612a8d90369060248160040135910161432d565b60008051602061569583398151915254936001600160401b03851680159081613022575b6001149081613018575b15908161300f575b50612ffd5760016001600160401b03198616176000805160206156958339815191525560ff8560401c1615612fd0575b612afb6150f1565b612b036150f1565b6001600160a01b03811615612fb757612b1b90614943565b60405194612b288661424c565b600e86526d13195d1d195c93d990dc99591a5d60921b602087015260405195612b508761424c565b60018752603160f81b6020880152612b666150f1565b612b6e6150f1565b8051906001600160401b038211611a35578190612b996000805160206155f583398151915254614b6f565b601f8111612f36575b50602090601f8311600114612ea457600092612e99575b50508160011b916000199060031b1c1916176000805160206155f5833981519152555b85516001600160401b038111611a3557612c0460008051602061563583398151915254614b6f565b601f8111612e23575b506020601f8211600114612d8a57918161287e92612cc3959460ff9a600092612d7f575b50508160011b916000199060031b1c191617600080516020615635833981519152555b60007fa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d1005560007fa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d10155612ca46150f1565b612cac6150f1565b60016000805160206156758339815191525561485d565b600180546001600160a01b03199081166001600160a01b039485161790915560028054909116919092161790556000805465ffffffffffff60601b191660843560701b63ffffffff60701b161760643560601b61ffff60601b1617905560401c1615612d2b57005b60ff60401b196000805160206156958339815191525416600080516020615695833981519152557fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2602060405160018152a1005b015190508a80612c31565b6000805160206156358339815191526000526000805160206156d58339815191529760005b601f1984168110612e0b57508260ff99612cc396959360019361287e96601f19811610612df2575b505050811b0160008051602061563583398151915255612c54565b015160001960f88460031b161c191690558a8080612dd7565b828201518a5560019099019860209283019201612daf565b600080516020615635833981519152600052601f820160051c6000805160206156d58339815191520160208310612e84575b601f820160051c6000805160206156d5833981519152018110612e785750612c0d565b60008155600101612e55565b506000805160206156d5833981519152612e55565b015190508880612bb9565b92506000805160206155f5833981519152600052600080516020615655833981519152906000935b601f1984168510612f1b576001945083601f19811610612f02575b505050811b016000805160206155f583398151915255612bdc565b015160001960f88460031b161c19169055888080612ee7565b81810151835560209485019460019093019290910190612ecc565b9091506000805160206155f5833981519152600052601f830160051c6000805160206156558339815191520160208410612fa2575b908392915b601f820160051c600080516020615655833981519152018110612f935750612ba2565b60008155849350600101612f70565b50600080516020615655833981519152612f6b565b604051631e4fbdf760e01b815260006004820152602490fd5b68ffffffffffffffffff198516680100000000000000011760008051602061569583398151915255612af3565b60405163f92ee8a960e01b8152600490fd5b90501587612ac3565b303b159150612abb565b604087901c60ff16159150612ab1565b346103cc5760203660031901126103cc57602460207f044dcf2257e109d3359aa04668eaf3065abf983bf9a013395d4edab6633c023661307061410e565b6130786147df565b600154604080516001600160a01b0380841682529390931660208401819052938493909190a16001600160a01b031916176001556040516301ffc9a760e01b81526316e15aeb60e21b600482015292839182905afa600091816130ea575b506130e357610bc5614507565b15610c2057005b61310491925060203d602011610c5f57610c508183614267565b90826130d6565b346103cc5760003660031901126103cc576000805160206156b583398151915254336001600160a01b03909116036131465761288333614943565b60405163118cdaa760e01b8152336004820152602490fd5b346103cc5760003660031901126103cc5760206040517fb2c9dd2d7f83ecfa5c4f5d7679b1f3acfbb0d53f15dccf8b1524979826eaa6c78152f35b346103cc5760003660031901126103cc576131b26147df565b6000805160206156b583398151915280546001600160a01b0319908116909155600080516020615615833981519152805491821690556000906001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b346103cc5760203660031901126103cc576004356001600160401b038082116103cc57366023830112156103cc5781600401359081116103cc573660248260071b840101116103cc576128839161327d916132736147df565b602436920161429f565b61485d565b60a03660031901126103cc576132966141a7565b61329e614124565b906001600160401b03906044358281116103cc576132c090369060040161417a565b9190926064358181116103cc576132db90369060040161417a565b50506084359081116103cc576132f590369060040161417a565b93909247946133026145f4565b6001600160601b0384166000526003602052604060002094600760405196613329886141cc565b80546001600160601b038116895260601c6020890152600181015460018060a01b03811660408a015263ffffffff8160a01c1660608a015261ffff90818160c01c1660808b015260d01c1660a089015260018060a01b0360028201541660c089015260018060a01b0360038201541660e08901526004810154610100890152600581015461012089015260018060a01b0360068201541661014089015201548061016088015215613a295760e08601516101408701516001600160a01b03908116911614613a085763ffffffff606087015116428111156139d9575061016086015160e08701516101408801516001600160a01b03918216969116946000949261344292909161343b91369190614468565b9089614e0d565b94608086015192865115806139d1575b6139b0576080870151801580156139a4575b61398257506001600160a01b038b161561386a5760c0890151895160408051637500616d60e01b81526001600160601b03909216600483015260248201879052306044830152600160648301528d92829060849082906000906001600160a01b03165af180156106f8576134e69160009161384a575b506101008c01516144e2565b5060405163095ea7b360e01b60208201526001600160a01b03821660248201526044810186905261352481606481015b03601f198101835282614267565b60008082516020840182885af1613539614507565b8161381b575b5080613811575b156137cd575b506040516370a0823160e01b8152306004820152946020866024818b5afa9586156106f857600096613799575b506020890151926001600160a01b0383163b156103cc576000946135d88692604051988997889687956335d090f360e11b8752336004880152602487015260448601528d6064860152608485015260c060a485015260c4840191614594565b03926001600160a01b03165af180156106f857613781575b50602060249293604051938480926370a0823160e01b82523060048301525afa801561165257839061374d575b61362792506144e2565b6020830151908181036116005750506106ce957f21640bc6d24e28d13a51d024a2c86f8dd94df3889ab646059044fad4cf4eded4915b6001600160601b038516815260036020526136f86003604080842093610160602089019a8b51806004890155600788015560018060a01b0361014082015116966002868201916001600160601b0360a01b928a848254161790558560058201556001810163ffffffff60c01b1981541690556001600160601b03198154168155019081541690550151948152600460205220019182546144e2565b9055604083810151606094850151965182519182526020820197909752908101959095526001600160a01b03169333936001600160601b03169290a460016000805160206156758339815191525534906144e2565b506020823d602011613779575b8161376760209383614267565b810103126103cc57613627915161361d565b3d915061375a565b6024925061378e9061421e565b6020600092506135f0565b9095506020813d6020116137c5575b816137b560209383614267565b810103126103cc5751948d613579565b3d91506137a8565b60405163095ea7b360e01b60208201526001600160a01b03831660248201526000604480830191909152815261380b91906116e1906116db816141e8565b8c61354c565b50833b1515613546565b8051801592508215613830575b50508e61353f565b61384392506020809183010191016144ef565b8e80613828565b613863915060403d6040116109705761095e8183614267565b508e6134da565b5050506138d59750604090929192339233613943575b5060c087015187518351637500616d60e01b81526001600160601b039091166004820152602481019290925233604483015260016064830152909889916001600160a01b031690829060009082906084820190565b03925af19182156106f85761391d7f21640bc6d24e28d13a51d024a2c86f8dd94df3889ab646059044fad4cf4eded4936106ce99600091613923575b506101008801516144e2565b5061365d565b61393c915060403d6040116109705761095e8183614267565b508a613911565b60208681015184516323b872dd60e01b92810192909252336024830152306044830152606482015261397c916118358260848101611827565b89613880565b60449060208901516040519163c661aab760e01b835260048301526024820152fd5b50602088015115613464565b604051639df5f2d760e01b81526001600160601b0389166004820152602490fd5b506001613452565b604051632cbdae0760e01b81526001600160601b038716600482015263ffffffff919091166024820152604490fd5b6040516332f1c16d60e11b81526001600160601b0386166004820152602490fd5b60405163810fbf1f60e01b81526001600160601b0386166004820152602490fd5b346103cc5760203660031901126103cc57613a6361410e565b613a6b6144bd565b5060018060a01b0316600052600460205260806040600020604051613a8f816141e8565b815491828252600181015460208301908152606060036002840154936040860194855201549301928352604051938452516020840152516040830152516060820152f35b346103cc5760003660031901126103cc576002546040516001600160a01b039091168152602090f35b346103cc5760203660031901126103cc5763ffffffff60043581811681036103cc57613b266147df565b60005460408051607083901c9490941663ffffffff90811685528316602085015290927faf14225344892ccd1d66a398b930d5d60e40729143c4f106f16f62df6a3090ac9190a163ffffffff60701b1990911660709190911b63ffffffff60701b1617600055005b346103cc5760403660031901126103cc57613ba761410e565b613baf614124565b90613bb86144bd565b5060018060a01b038091166000526005602052604060002091166000526020526080604060002060405190613bec826141e8565b5461ffff9081808216938481528160208201818560101c1681528160606040850194828860201c168652019560301c168552604051968752511660208601525116604084015251166060820152f35b346103cc5760c03660031901126103cc57613c5461410e565b613c5c614124565b63ffffffff60643516606435036103cc576001600160401b03916084358381116103cc57613c8e90369060040161417a565b91909360a4359081116103cc57613ca990369060040161417a565b949092613cb46145f4565b6102008087116140f0575080614047575b50504263ffffffff60643516111561402057613ce94263ffffffff606435166144e2565b63ffffffff60005460701c16809111613ff65750600154604080516307e8761360e41b81523360048201526001600160a01b038681166024830152604480359083015290969216929190866064816000875af19182156106f857602096600090600094613fae575b50613f88907f6b401e682129f6a19f416a78c37fd6d263bfc32f134438588451bfbbb7b9baed949560005490613d8f6001600160601b038316614755565b998a926001600160601b038416906001600160601b03191617600055604051613db7816141cc565b6001600160601b0385168152338d82015260018060a01b038716604082015263ffffffff60643516606082015260006080820152600060a08201528260c082015260018060a01b03821660e08201528961010082015260443561012082015260018060a01b0382166101408201526044356101608201526001600160601b03841660005260038d5260076101606040600020926001600160601b038151166001600160601b0319602083015160601b1617845560018401613f0460018060a01b03604084015116916001600160601b0360a01b9283825416178155613ec263ffffffff606086015116829081549063ffffffff60a01b9060a01b169063ffffffff60a01b1916179055565b6080840151815461ffff60c01b191660c09190911b61ffff60c01b1617815561ffff60a0850151825461ffff60d01b1916911660d01b61ffff60d01b16179055565b60c082015160028601805483166001600160a01b0392831617905560e0830151600387018054841691831691909117905561010083015160048701556101208301516005870155610140830151600687018054909316908216179091559101519190920155604051961699339996978897604435946064359386938692918c61476d565b0390a36001600080516020615675833981519152556001600160601b0360405191168152f35b7f6b401e682129f6a19f416a78c37fd6d263bfc32f134438588451bfbbb7b9baed9450613f889150613fee9060403d60401161237e5761236e8183614267565b945090613d51565b60405163cf113d3160e01b815263ffffffff91821660048201526064359091166024820152604490fd5b604051632cbdae0760e01b81526000600482015260643563ffffffff166024820152604490fd5b6001546040516250fd9760e21b815292906001600160a01b0316602084600481845afa9384156106f85787906000956140ad575b5061252c6140a161271061409861ffff6140a69916604435614d65565b04604435614624565b614631565b8480613cc5565b919450506020813d6020116140e8575b816140ca60209383614267565b810103126103cc57519261ffff841684036103cc578661252c61407b565b3d91506140bd565b604490876040519163b2560b7960e01b835260048301526024820152fd5b600435906001600160a01b03821682036103cc57565b602435906001600160a01b03821682036103cc57565b606435906001600160a01b03821682036103cc57565b604435906001600160a01b03821682036103cc57565b35906001600160a01b03821682036103cc57565b9181601f840112156103cc578235916001600160401b0383116103cc57602083818601950101116103cc57565b600435906001600160601b03821682036103cc57565b359061ffff821682036103cc57565b61018081019081106001600160401b03821117611a3557604052565b608081019081106001600160401b03821117611a3557604052565b606081019081106001600160401b03821117611a3557604052565b6001600160401b038111611a3557604052565b60a081019081106001600160401b03821117611a3557604052565b604081019081106001600160401b03821117611a3557604052565b90601f801991011681019081106001600160401b03821117611a3557604052565b6001600160401b038111611a355760051b60200190565b9291926142ab82614288565b6040926142bb6040519283614267565b819581835260208093019160071b8401938185116103cc57915b8483106142e457505050505050565b6080838303126103cc578360809187516142fd816141e8565b61430686614166565b815282860135838201528886013589820152606080870135908201528152019201916142d5565b92919261433982614288565b60409461434886519283614267565b819584835260208093019160c08096028501948186116103cc57925b8584106143745750505050505050565b8382038781126103cc5783519161438a83614203565b61439386614166565b83526143a0878701614166565b8784015260808092603f1901126103cc57889287928651906143c1826141e8565b6143cc888a016141bd565b82526143ea6060916143df838c016141bd565b878501528a016141bd565b888301526143fa60a08a016141bd565b9082015286820152815201930192614364565b919082519283825260005b848110614439575050826000602080949584010152601f8019910116010190565b602081830181015184830182015201614418565b6001600160401b038111611a3557601f01601f191660200190565b9291926144748261444d565b916144826040519384614267565b8294818452818301116103cc578281602093846000960137010152565b9080601f830112156103cc578160206144ba93359101614468565b90565b604051906144ca826141e8565b60006060838281528260208201528260408201520152565b9190820391821161092a57565b908160209103126103cc575180151581036103cc5790565b3d15614532573d906145188261444d565b916145266040519384614267565b82523d6000602084013e565b606090565b6040519061454482614203565b60006040838281528260208201520152565b908160609103126103cc576040519061456e82614203565b805182526020810151908160030b82036103cc5760409160208401520151604082015290565b908060209392818452848401376000828201840152601f01601f1916010190565b6001600160a01b039182168152911660208201526060604082018190526144ba93910191614594565b91908260409103126103cc576020825192015190565b60008051602061567583398151915260028154146146125760029055565b604051633ee5aeb560e01b8152600490fd5b9190820180921161092a57565b6001600160ff1b0381116146425790565b6024906040519063ddc5fd2560e01b82526004820152fd5b90936001600160a01b03929091831690813b156103cc57600080946146b66040519788968795869463147c0f9b60e31b8652869c336004880152306024880152166044860152606485015260a0608485015260a4840191614594565b03925af1908161471a575b50614717576146ce614507565b9050602081519101805163ffffffff60e01b8082169160048510614702575b50166309ea8ec360e21b01614700575050565bfd5b818093508560040360031b1b161690386146ed565b50565b61472591925061421e565b600090386146c1565b91908260409103126103cc5781516001600160601b03811681036103cc5760209092015190565b6001600160601b0380911690811461092a5760010190565b9997939b9a96919463ffffffff936147966101209b98936020988e610140908181520191614594565b9d8c60018060a01b0398898094169101521660408c015260608b015260808a01521660a08801521660c086015260e08501526001600160601b0380921661010085015216910152565b600080516020615615833981519152546001600160a01b0316330361314657565b6001600160601b03909116815260208101919091526001600160a01b039091166040820152901515606082015260800190565b80518210156148475760209160051b010190565b634e487b7160e01b600052603260045260246000fd5b9060009160005b815181101561493d576148778183614833565b51602080820151604091828401519288606060018060a01b038751169601519283614904575b92828160027fe8717d5a73c7565350b82e71c4d5e4e746eaded98792418833b20ca1a5a8c67597958560048c9b9960019e9d6148fb99525220868155878c820155015551938493846040919493926060820195825260208201520152565b0390a201614864565b90508484101561492c57831561491b57899061489d565b81516377fb84ef60e01b8152600490fd5b8151630ff84e1560e31b8152600490fd5b50509050565b6000805160206156b583398151915280546001600160a01b031990811690915560008051602061561583398151915280549182166001600160a01b0393841690811790915591167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a3565b60005b8151811015614b6b576149c78183614833565b5160409081810151918083019261ffff938481511661271093848211614b54578684511694808611614b3c57602080860195898751169288841115614b1957839003968a881161092a578a881697888711614af557606097888401918d8351169a8b11614ad157508b85600160a01b60019003809251169d0151169c8c600052600586528d88600020906000528652876000209451168454935160101b63ffff0000169151861b65ffff0000000016925160301b67ffff00000000000016936001600160401b031916171717179055825196875286015284015282015260807f0fc3464bbc911b6f91c84b623aad53d301ba43fd3970579a4d57963d9b71797d91a36001016149b4565b87516311c78d9360e01b815261ffff9182166004820152908b166024820152604490fd5b855163118466cf60e21b815261ffff91821660048201529087166024820152604490fd5b845163ba743e6d60e01b815261ffff8a8116600483015285166024820152604490fd5b8151633b21b52f60e11b815260048101879052602490fd5b602492505190633b21b52f60e11b82526004820152fd5b5050565b90600182811c92168015614b9f575b6020831014614b8957565b634e487b7160e01b600052602260045260246000fd5b91607f1691614b7e565b604051906000826000805160206155f583398151915291825492614bcc84614b6f565b80845293602091600191828116908115614c495750600114614bfa575b505050614bf892500383614267565b565b60009081526000805160206156558339815191529590935091905b828410614c315750614bf8945050508101602001388080614be9565b85548885018301529485019487945092810192614c15565b9250505060209250614bf894915060ff191682840152151560051b820101388080614be9565b6040519060008260008051602061563583398151915291825492614c9284614b6f565b80845293602091600191828116908115614c495750600114614cbd57505050614bf892500383614267565b60009081526000805160206156d58339815191529590935091905b828410614cf45750614bf8945050508101602001388080614be9565b85548885018301529485019487945092810192614cd8565b63ffffffff164281111580614d4a575b614d235750565b60449061ffff60005460601c166040519163196172bf60e11b835260048301526024820152fd5b50614d5581426144e2565b61ffff60005460601c1610614d1c565b8181029291811591840414171561092a57565b8115614d82570490565b634e487b7160e01b600052601260045260246000fd5b91614da391926152d5565b8015614dda576127109182810292818404149015171561092a57614dc691614d78565b61ffff9081811115614dd6575090565b1690565b505061ffff90565b60405190614def82614231565b60006080838281528260208201528260408201528260608201520152565b91614e16614de2565b506000614e21614537565b5081511561506e575060025460e084015161014085015160405163b37b327160e01b81526001600160a01b0392831660048201529082166024820152606060448201819052909390928492169082908190614e8090606483019061440d565b039134905af19081156106f85760009161504f575b505b604090614eac63ffffffff8383015116614d0c565b60018060a01b038060e08601511660005260056020528260002090610140860151166000526020528160002091805192614ee5846141e8565b549361ffff9586861685526020850195878160101c16875287606085880197828460201c168952019160301c168152610120820196614f258689516152d5565b801561503e57614f39908a8951169061533d565b91610160840192835180821061500d575050614f5b61010085019788516152d5565b9251612710928382029180830485149015171561092a5784614fb29388614faa938f614f89819a8298614d78565b9151161180159f9061500157614fa592508d51169b5b51614d65565b614d78565b971687614d65565b0495614fbe8787614624565b97518811614fec57505050815195614fd587614231565b865260208601528401526060830152608082015290565b93509350945092506144ba945051169161536b565b614fa59251169b614f9f565b9594995097959a9996505050500361502d576144ba94955051169161536b565b85516384cd6fa160e01b8152600490fd5b85516342486e5160e01b8152600490fd5b615068915060603d60601161084d5761083f8183614267565b38614e95565b60025460e0850151610140860151604051635620c32d60e11b81526001600160a01b0392831660048201529082166024820152929350606091839160449183918791165af19182156150e557916150c6575b50614e97565b6150df915060603d60601161084d5761083f8183614267565b386150c0565b604051903d90823e3d90fd5b60ff6000805160206156958339815191525460401c161561510e57565b604051631afcd79f60e31b8152600490fd5b6151286154e2565b61513061554c565b916040519260208401927f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f8452604085015260608401524660808401523060a084015260a0835260c08301918383106001600160401b03841117611a355760429360e291846040528151902061190160f01b855260c282015201522090565b6151b9838361542c565b50600481959295101561527857159384615262575b5083156151dc575b50505090565b6000929350908291604051615215816135166020820194630b135d3f60e11b998a8752602484015260406044840152606483019061440d565b51915afa90615222614507565b82615254575b82615238575b50503880806151d6565b9091506020818051810103126103cc576020015114388061522e565b915060208251101591615228565b6001600160a01b038381169116149350386151ce565b634e487b7160e01b600052602160045260246000fd5b60405163a9059cbb60e01b60208201526001600160a01b0390921660248301526044820192909252614bf8916118358260648101611827565b604d811161092a57600a0a90565b60208201906000825160030b1260001461531b576152f591925190614d65565b905160030b600003908160030b91820361092a576153156144ba926152c7565b90614d78565b61532d615337916144ba945190614d65565b915160030b6152c7565b90614d65565b6127108082029180830482149015171561092a5761ffff809316019180831161092a576144ba921690614d78565b6101206153879193929361537d614de2565b500151918261533d565b61539181836144e2565b906040519361539f85614231565b60018552602085015260408401526060830152608082015290565b6000806153e39260018060a01b03169360208151910182865af16153dc614507565b9083615591565b8051908115159182615411575b50506153f95750565b60249060405190635274afe760e01b82526004820152fd5b61542492506020809183010191016144ef565b1538806153f0565b815191906041830361545d5761545692506020820151906060604084015193015160001a90615468565b9192909190565b505060009160029190565b91906fa2a8918ca85bafe22016d0b997e4df60600160ff1b0384116154d657926020929160ff608095604051948552168484015260408301526060820152600092839182805260015afa156150e55780516001600160a01b038116156154cd57918190565b50809160019190565b50505060009160039190565b6154ea614ba9565b80519081156154fa576020012090565b50507fa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d1005480156155275790565b507fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47090565b615554614c6f565b8051908115615564576020012090565b50507fa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d1015480156155275790565b906155b857508051156155a657805190602001fd5b604051630a12f52160e11b8152600490fd5b815115806155eb575b6155c9575090565b604051639996b31560e01b81526001600160a01b039091166004820152602490fd5b50803b156155c156fea16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d1029016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300a16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d10342ad5d3e1f2e6e70edcf6d991b8a3023d3fca8047a131592f9edb9fd9b89d57d9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00f0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00237e158222e3e6968b72b9db0d8043aacf074ad9f650f0d1606b4d82ee432c005f9ce34815f8e11431c7bb75a8e6886a91478f7ffc1dbb0a98dc240fddd76b75a26469706673582212200f82a9e805f4c7bce863baa2d530c7baf1159d9af2e4175da16e1ef76042d6de64736f6c63430008190033","optimization_enabled":true,"verified_twin_address_hash":null,"is_verified":true,"compiler_settings":{"evmVersion":"paris","libraries":{},"optimizer":{"enabled":true,"runs":166},"outputSelection":{"*":{"":["*"],"*":["*"]}},"viaIR":true},"optimization_runs":166,"sourcify_repo_url":null,"decoded_constructor_args":null,"compiler_version":"v0.8.25+commit.b61c2a91","is_verified_via_verifier_alliance":false,"verified_at":"2026-08-05T20:08:38.350244Z","implementations":[],"proxy_type":null,"external_libraries":[],"creation_bytecode":"0x6080806040523460b4577ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a009081549060ff8260401c1660a557506001600160401b036002600160401b0319828216016061575b60405161572a90816100ba8239f35b6001600160401b031990911681179091556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290602090a13880806052565b63f92ee8a960e01b8152600490fd5b600080fdfe6080604052600436101561001257600080fd5b60003560e01c8063052e71a014613c3b5780630b24ec6814613b8e5780631008448314613afc5780632630c12f14613ad35780634e665cad14613a4a5780635d83d1f51461328257806362d751bd1461321a578063715018a61461319957806373fca6ea1461315e57806379ba50971461310b5780637e568658146130325780637f691199146129e157806384b0196e1461288557806384b9bc9f1461281b57806385a99879146127e0578063896797a6146127bb5780638da5cb5b146127855780639c9fb5fb14612590578063ac36c5d114612569578063c10fadb814611e23578063c1c7816e14611e06578063c6e1c7c914611ddd578063ce85428714611a4b578063d06d883414610cdf578063d723365114610c78578063dc9e0faa14610b4d578063e085a0f514610a80578063e30c397814610a4a578063eba8f9fd14610458578063f2fde38b146103d15763fd948dea1461017157600080fd5b346103cc5760403660031901126103cc5761018a6141a7565b60243563ffffffff91828216928383036103cc576001600160601b0393848316948560005260036020526040600020604051916101c6836141cc565b81549081168352602083019060601c8152610160600183015493600760018060a01b039485871660408401526060830196898160a01c16885261ffff90818160c01c16608086015260d01c1660a08401528560028201541660c08401528560038201541660e0840152600481015461010084015260058101546101208401528560068201541661014084015201549182910152156103b3575116330361038c57518216928382111561035d574284111561032e57506102869042906144e2565b9060005460701c1680911161030757506000838152600360205260409020600101805463ffffffff60a01b191660a084901b63ffffffff60a01b161790557fcbe79b0f0cfd0f9b04e2c1db6a49dd1ed5805d75196ca39b32f61d51aae6dca991906040805163ffffffff9283168152929091166020830152819081010390a2005b60405163cf113d3160e01b815263ffffffff91821660048201529216602483015250604490fd5b604051632cbdae0760e01b81526001600160601b0391909116600482015263ffffffff84166024820152604490fd5b604051632207376560e01b81526001600160601b0391909116600482015263ffffffff85166024820152604490fd5b604051636bcb678560e11b81523360048201526001600160601b0385166024820152604490fd5b60405163810fbf1f60e01b815260048101899052602490fd5b600080fd5b346103cc5760203660031901126103cc576103ea61410e565b6103f26147df565b6000805160206156b583398151915280546001600160a01b0319166001600160a01b03928316908117909155600080516020615615833981519152549091167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e22700600080a3005b60803660031901126103cc5761046c6141a7565b6001600160401b03906044358281116103cc5761048d90369060040161417a565b926064359081116103cc576104a690369060040161417a565b939047926104b26145f4565b6001600160601b03851660005260036020526040600020916007604051936104d9856141cc565b80546001600160601b038116865260601c602086015261ffff600182015460018060a01b038116604088015263ffffffff8160a01c166060880152818160c01c16608088015260d01c1660a086015260018060a01b0360028201541660c086015260018060a01b0360038201541660e08601526004810154610100860152600581015461012086015260018060a01b03600682015416610140860152015461016084015260243515610a395761016083015115610a185760208301516001600160a01b031633036109f15760e08301516101408401516001600160a01b039081169116146109d05763ffffffff60608401511696428811156109a35760009596975080610977575b5050604060018060a01b0360c08401511660446001600160601b03855116835197889384926360b05e7160e01b8452600484015260243560248401525af19283156106f857600094600094610940575b506000602435131561070d575b5050846106b27f7ff335afdf1474dc98fe86cdafdd3882799b79b4a48004d6e766e2615ef22765936101006001600160601b0394856106ce9a16600052600360205282600560406000208a600482015501550151956040519485941696846040919493926060820195825260208201520152565b0390a260016000805160206156758339815191525534906144e2565b8047116106d757005b600080806106e68194476144e2565b818115610704575b3390f1156106f857005b6040513d6000823e3d90fd5b506108fc6106ee565b602435600160ff1b1461092a5761010083015180602435600003101561090a575060e08301516001600160a01b0390811660009081526005602090815260408083206101408801519094168352929052205461ffff169182156108d657610772614537565b5081156108545760025460e085015161014086015160405163b37b327160e01b81529460609486946001600160a01b0390811694869485946107c094909390929181169116600486016145b5565b039134905af19081156106f85761ffff916107fe91600091610825575b505b6107f263ffffffff604083015116614d0c565b61016085015187614d98565b168181111561063e576044925060405191632c1f8ef160e21b835260048301526024820152fd5b610847915060603d60601161084d575b61083f8183614267565b810190614556565b896107dd565b503d610835565b505060025460e0830151610140840151604051635620c32d60e11b81526001600160a01b03928316600482015290821660248201529160609183916044918391600091165af19081156106f85761ffff916107fe916000916108b7575b506107df565b6108d0915060603d60601161084d5761083f8183614267565b896108b1565b60e0840151610140850151604051636a6fc48160e01b81526001600160a01b03928316600482015291166024820152604490fd5b60449060405190632c1f8ef160e21b825260243560048301526024820152fd5b634e487b7160e01b600052601160045260246000fd5b90935061096691945060403d604011610970575b61095e8183614267565b8101906145de565b9390939287610631565b503d610954565b60c084015160e085015161099c9391602435916001600160a01b03908116911661465a565b86806105e1565b604051632cbdae0760e01b81526001600160601b038816600482015263ffffffff89166024820152604490fd5b6040516332f1c16d60e11b81526001600160601b0387166004820152602490fd5b604051636bcb678560e11b81523360048201526001600160601b0387166024820152604490fd5b60405163810fbf1f60e01b81526001600160601b0387166004820152602490fd5b60405162219cf160e01b8152600490fd5b346103cc5760003660031901126103cc576000805160206156b5833981519152546040516001600160a01b039091168152602090f35b346103cc5760203660031901126103cc5761ffff6004358181168082036103cc57610aa96147df565b601e81108015610b42575b610b1b575060005460408051606083901c9490941661ffff90811685528316602085015290927fff2f93c7ba57f9142fd98325a32e23b17b30d1d203f406d7eb04a55b5c3957ac9190a161ffff60601b1990911660609190911b61ffff60601b1617600055005b606490604051906310279acb60e31b8252601e6004830152610e1060248301526044820152fd5b50610e108111610ab4565b346103cc5760203660031901126103cc57610b6661410e565b610b6e6147df565b6002546001600160a01b03918216918116828114610a39578215610c66576040516301ffc9a760e01b8152633cb4bea160e01b6004820152602081602481875afa60009181610c35575b50610bdb57610bc5614507565b506040516001621a684d60e01b03198152600490fd5b15610c205760407fed5e4ea1344427f93380f9247ae9da395842832daa39465284c4274ac799ac52918151908152846020820152a16001600160a01b03191617600255005b6040516001621a684d60e01b03198152600490fd5b610c5891925060203d602011610c5f575b610c508183614267565b8101906144ef565b9085610bb8565b503d610c46565b60405163f6b2911f60e01b8152600490fd5b346103cc5760403660031901126103cc576001600160a01b03610c9961410e565b166000527fa10463db4f5369b35ba0ad7c2c4710776052e62babfb578b03cd47bf9aa4f10060205260406000206024356000526020526020604060002054604051908152f35b60e03660031901126103cc57610cf36141a7565b610cfb614150565b90610d0461413a565b906084356001600160401b0381116103cc57610d2490369060040161417a565b939060a4356001600160401b0381116103cc57610d4590369060040161449f565b9060c4356001600160401b0381116103cc57610d6590369060040161417a565b9390914796610d726145f4565b6001600160601b0387166000526003602052604060002060405195610d96876141cc565b81546001600160601b0381168852606090811c602089015260018301546001600160a01b0380821660408b0181905260a083811c63ffffffff16948c019490945260c083811c61ffff90811660808e015260d085901c16948c019490945260028601548216938b01939093526003850154811660e08b015260048501546101008b015260058501546101208b01526006850154166101408a015260079093015461016089018190529190330361191c575b505050610160850151156118fb5763ffffffff606086015116428111156118cc57506024351580156118bc575b611897576001600160a01b03831615610c665761016085015160e086015161014087015160009b60243599938a14978d9690946001600160a01b03948516949293909216908185036111e7575b5050505050506001600160601b038551161515806111c8575b15611074575050610f24965060408260018060a01b0360c0860151168360006001600160601b0388511685519c8d9586948593637500616d60e01b85526024359060048601614800565b03925af19283156106f8576106ce977fa401b77f6ebc213dba24e20584bca0389bbd6391cf1498bbbf4feac74932f68a946001600160601b0394610fb993600093600093611045575b5083610100610f7d9201516144e2565b9160243593821561101d575b50505b610fd5575b60405194859460018060a01b0316981696846040919493926060820195825260208201520152565b0390a360016000805160206156758339815191525534906144e2565b8488166000526003602052611018604060002060076000918281558260018201558260028201558260038201558260048201558260058201558260068201550155565b610f91565b600790888c166000526003602052604060002090600482015582600582015501558b80610f89565b6101009450610f7d9193506110689060403d6040116109705761095e8183614267565b90945092905083610f6d565b936106ce989391929482156000146111785792610fb9926001600160601b03959287957fa401b77f6ebc213dba24e20584bca0389bbd6391cf1498bbbf4feac74932f68a9860243591861561112e575b898451161590811561111b575b508015611108575b6110e6575b505050610f8c565b6101409092015161110092906001600160a01b031661528e565b8b80866110de565b506001600160a01b0381163314156110d9565b6001600160a01b0316151590508f6110d1565b945050878b166000526003602052866040600020946111546024356101608601516144e2565b60078701556024359560046111706024356101008801516144e2565b9101556110c4565b9550927fa401b77f6ebc213dba24e20584bca0389bbd6391cf1498bbbf4feac74932f68a9492610fb9926001600160601b0395868a16600052600360205260076040600020015498868a916110c4565b5060e08501516101408601516001600160a01b03908116911614610eda565b6112019193959e5061120a92949697506000953691614468565b6024358a614e0d565b926080840151948451158061188f575b61186e57608085015180158015611862575b61184057506001600160a01b0387161561174757869361128260408b8d6001600160601b0360018060a01b0360c08301511691511660008c855196879586948593637500616d60e01b8552309160048601614800565b03925af180156106f8576112a491600091611727575b506101008d01516144e2565b9e886040516000808b6112f1846112e3602082019363095ea7b360e01b988986526024840160209093929193604081019460018060a01b031681520152565b03601f198101865285614267565b83519082895af1611300614507565b816116f8575b50806116ee575b156116a9575b50506040516370a0823160e01b815230600482015292602084602481895afa9384156106f857600094611675575b506020880151906001600160a01b038b163b156103cc5761139d60009360405195869485946335d090f360e11b865233600487015260248601528d60448601528a6064860152608485015260c060a485015260c4840191614594565b0381836001600160a01b038d165af180156106f85761165d575b50602060249293604051938480926370a0823160e01b82523060048301525afa801561165257839061161e575b6113ee92506144e2565b602084015190818103611600575050918991888d945b896114ff57906114b5600360407f103624043a1a382761fb8e47bd03d967b68fce58c92eabd1c990478babf49d8b95946001600160601b0389168152826020526114568282209a6101008701516144e2565b60048b015561146f61012086015160808a0151906144e2565b60058b015561016085015199600761148d60208b019c8d51906144e2565b9101558951610140909501516001600160a01b031681526004602052200180549092906144e2565b9055604083810151606094850151965182519182526020820197909752908101959095526001600160a01b03169333936001600160601b03169290a45b9790898080808080610ec1565b7f21640bc6d24e28d13a51d024a2c86f8dd94df3889ab646059044fad4cf4eded4929195506001600160601b038516815260036020526115bf6003604080842093610160602089019a8b51806004890155600788015560018060a01b0361014082015116966002868201916001600160601b0360a01b928a848254161790558560058201556001810163ffffffff60c01b1981541690556001600160601b03198154168155019081541690550151948152600460205220019182546144e2565b9055604083810151606094850151965182519182526020820197909752908101959095526001600160a01b03169333936001600160601b03169290a46114f2565b604492506040519163799fce1f60e11b835260048301526024820152fd5b506020823d60201161164a575b8161163860209383614267565b810103126103cc576113ee91516113e4565b3d915061162b565b6040513d85823e3d90fd5b6024925061166a9061421e565b6020600092506113b7565b9093506020813d6020116116a1575b8161169160209383614267565b810103126103cc57519238611341565b3d9150611684565b6116e16116e79260405190602082015260018060a01b038d16602482015260006044820152604481526116db816141e8565b866153ba565b846153ba565b3880611313565b50843b151561130d565b805180159250821561170d575b505038611306565b61172092506020809183010191016144ef565b3880611705565b611740915060403d6040116109705761095e8183614267565b5038611298565b50979b506117a39792339250906001600160a01b0387168390036117f1575b506040878d6001600160601b0360018060a01b0360c08301511691511660008451809d81958294637500616d60e01b84528c339160048601614800565b03925af19182156106f8576117c98b938e9a6000916117d1575b506101008b01516144e2565b9c8d94611404565b6117ea915060403d6040116109705761095e8183614267565b508f6117bd565b6020828101516040516323b872dd60e01b92810192909252336024830152306044830152606482015261183a9161183582608481015b03601f198101845283614267565b6153ba565b8c611766565b60449060208701516040519163c661aab760e01b835260048301526024820152fd5b5060208601511561122c565b604051639df5f2d760e01b81526001600160601b038d166004820152602490fd5b50600061121a565b60446101608601516040519063153360a160e01b825260243560048301526024820152fd5b5061016085015160243511610e74565b604051632cbdae0760e01b81526001600160601b038916600482015263ffffffff919091166024820152604490fd5b60405163810fbf1f60e01b81526001600160601b0388166004820152602490fd5b60018060a01b0383166000527fa10463db4f5369b35ba0ad7c2c4710776052e62babfb578b03cd47bf9aa4f1006020526040600020917f15cdf564776febe3f454b055f4b652fb3b48292468cdcbbc896f19f1f5bd7cad92836000526020526040600020908154916001830190556040519360208501526001600160601b038c1660408501526024356060850152608084015260018060a01b03871660a084015260c083015260c082528160e08101106001600160401b0360e084011117611a35576119f88260e0611a07940160405260208151910120615120565b6001600160a01b0384166151af565b15611a13578080610e47565b604051633615713d60e21b81526001600160a01b039091166004820152602490fd5b634e487b7160e01b600052604160045260246000fd5b346103cc5760403660031901126103cc57611a646141a7565b6024356001600160401b0381116103cc57611a8390369060040161449f565b90611a8c6145f4565b6001600160601b0381166000526003602052604060002060405192611ab0846141cc565b81546001600160601b0381168552606090811c602086015260018301546001600160a01b038082166040880181905260a083811c63ffffffff169489019490945260c083811c61ffff90811660808b015260d085901c169489019490945260028601548216938801939093526003850154811660e08801526004850154610100880152600585015461012088015260068501541661014087015260079093015461016086015233141580611dcb575b611d19575b505061016082015115611cf7576001600160601b0381166000526003602052611bbb604060002060076000918281558260018201558260028201558260038201558260048201558260058201558260068201550155565b81516001600160601b031680611c80575060e082015160208301516101008401516001600160601b039392611bfc926001600160a01b03908116911661528e565b167f1706888457c949ef810b6308f2cb8280ecb6d22a992c4b53cf15f25405020c10600080a261014081015160e08201516001600160a01b039182169291168203611c56575b600160008051602061567583398151915255005b6101600151906000526004602052611c7760036040600020019182546144e2565b90558080611c42565b60c0830151604051630834ef4b60e21b81526004810192909252602090829060249082906000906001600160a01b03165af180156106f857611ccc575b506001600160601b0390611bfc565b602090813d8311611cf0575b611ce28183614267565b810103126103cc5782611cbd565b503d611cd8565b60405163810fbf1f60e01b81526001600160601b039091166004820152602490fd5b611dc09060018060a01b0383166000527fa10463db4f5369b35ba0ad7c2c4710776052e62babfb578b03cd47bf9aa4f1006020526119f860406000207fb2c9dd2d7f83ecfa5c4f5d7679b1f3acfbb0d53f15dccf8b1524979826eaa6c79081600052602052604060002080549060018201905560405190602082019283526001600160601b0388166040830152606082015260608152611db8816141e8565b519020615120565b15611a135780611b64565b504263ffffffff8360a01c1611611b5f565b346103cc5760003660031901126103cc576001546040516001600160a01b039091168152602090f35b346103cc5760003660031901126103cc5760206040516102008152f35b6101203660031901126103cc57611e3861410e565b611e40614124565b90611e4961413a565b9163ffffffff60a4351660a435036103cc576001600160401b0360c4358181116103cc57611e7b90369060040161417a565b929060e4358381116103cc57611e9590369060040161417a565b949093610104359081116103cc57611eb190369060040161417a565b9290944796611ebe6145f4565b6001600160a01b03868116908b16146125575761020080861161253957508061250f575b5050611eec614537565b50811561249f5760025460405163b37b327160e01b81529260609284926001600160a01b03169183918291611f26918e8b600486016145b5565b039134905af19081156106f857600091612480575b505b4263ffffffff60a43516111561245957611f5f4263ffffffff60a435166144e2565b63ffffffff60005460701c1680911161242f5750611f8663ffffffff604083015116614d0c565b60018060a01b03871660005260046020526040600020604051611fa8816141e8565b8154815260018201549081602082015260036002840154936040830194855201549160608201928352806084351161240f57505180608435106123ef5750611ff4906084359051614624565b90518082116123d257506001600160a01b038881166000818152600460209081526040808320600301959095559287168152600583528381209181529152205461ffff169081156123a95761204e90608435604435614d98565b908061ffff8316116123855750506001546040805163a2e115e960e01b81523360048201526001600160a01b038581166024830152604480359083015290979288916064918391600091165af19081156106f857612304967f6b401e682129f6a19f416a78c37fd6d263bfc32f134438588451bfbbb7b9baed9360009160009461234e575b50600054610fb992916120ee6001600160601b038316614755565b6bffffffffffffffffffffffff199092166001600160601b03831617600055600154604051929c8d9390916001600160a01b031661212b826141cc565b6001600160601b038616825233602083015260018060a01b038816604083015263ffffffff60a43516606083015260006080830152600060a083015260c082015260018060a01b038a1660e08201526044356101008201528761012082015260018060a01b0382166101408201526084356101608201526001600160601b038416600052600360205260076101606040600020926001600160601b038151166001600160601b0319602083015160601b161784556001840161227760018060a01b03604084015116916001600160601b0360a01b928382541617815561223763ffffffff606086015116829081549063ffffffff60a01b9060a01b169063ffffffff60a01b1916179055565b6080840151815461ffff60c01b191660c09190911b61ffff60c01b1617815560a0840151815461ffff60d01b191660d09190911b61ffff60d01b16179055565b60c082015160028601805483166001600160a01b0392831617905560e08301516003870180548416918316919091179055610100830151600487015561012083015160058701556101408301516006870180549093169082161790915591015191909201556001546040519782169b339b98998a996084359660a43595929460443594929316918c61476d565b804711612320575b6020826001600160601b0360405191168152f35b6000808061232f8194476144e2565b818115612345575b3390f1156106f8578161230c565b506108fc612337565b612376919450610fb9925060403d60401161237e575b61236e8183614267565b81019061472e565b9390916120d3565b503d612364565b604051630ed2e35760e41b815261ffff918216600482015291166024820152604490fd5b604051636a6fc48160e01b81526001600160a01b03858116600483015289166024820152604490fd5b6044916040519163eee9771b60e01b835260048301526024820152fd5b60449060405190635ac8b2d960e01b825260048201526084356024820152fd5b6044906040519063cfb00aa360e01b825260048201526084356024820152fd5b60405163cf113d3160e01b815263ffffffff918216600482015260a4359091166024820152604490fd5b604051632cbdae0760e01b81526000600482015260a43563ffffffff166024820152604490fd5b612499915060603d60601161084d5761083f8183614267565b87611f3b565b5050600254604051635620c32d60e11b81526001600160a01b0384811660048301528881166024830152909160609183916044918391600091165af19081156106f8576000916124f0575b50611f3d565b612509915060603d60601161084d5761083f8183614267565b876124ea565b60015461253292906001600160a01b03168761252c604435614631565b9161465a565b8880611ee2565b604490866040519163b2560b7960e01b835260048301526024820152fd5b6040516314b7e24960e31b8152600490fd5b346103cc5760003660031901126103cc57602063ffffffff60005460701c16604051908152f35b346103cc5760203660031901126103cc576001600160601b036125b16141a7565b60006101606040516125c2816141cc565b8281528260208201528260408201528260608201528260808201528260a08201528260c08201528260e082015282610100820152826101208201528261014082015201521660005260036020526101806040600020610160604051612626816141cc565b60078354936001600160601b03851683528460601c602084015261ffff600182015460018060a01b038116604086015263ffffffff8160a01c166060860152818160c01c16608086015260d01c1660a084015260018060a01b0360028201541660c084015260018060a01b0360038201541660e08401526004810154610100840152600581015461012084015260018060a01b036006820154166101408401520154828201526001600160601b036040519316835260018060a01b03602082015116602084015260018060a01b03604082015116604084015263ffffffff606082015116606084015261ffff608082015116608084015261ffff60a08201511660a084015260018060a01b0360c08201511660c084015260018060a01b0360e08201511660e084015261010081015161010084015261012081015161012084015260018060a01b03610140820151166101408401520151610160820152f35b346103cc5760003660031901126103cc57600080516020615615833981519152546040516001600160a01b039091168152602090f35b346103cc5760003660031901126103cc57602061ffff60005460601c16604051908152f35b346103cc5760003660031901126103cc5760206040517f15cdf564776febe3f454b055f4b652fb3b48292468cdcbbc896f19f1f5bd7cad8152f35b346103cc5760203660031901126103cc576004356001600160401b038082116103cc57366023830112156103cc5781600401359081116103cc5736602460c08302840101116103cc576128839161287e916128746147df565b602436920161432d565b6149b1565b005b346103cc5760003660031901126103cc577fa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d1005415806129b8575b1561297b576128cc614ba9565b6128d4614c6f565b6040516020808201928284106001600160401b03851117611a355791602061292e8594612920979660405260008452604051978897600f60f81b895260e0858a015260e089019061440d565b90878203604089015261440d565b91466060870152306080870152600060a087015285830360c0870152519182815201929160005b82811061296457505050500390f35b835185528695509381019392810192600101612955565b60405162461bcd60e51b81526020600482015260156024820152741152540dcc4c8e88155b9a5b9a5d1a585b1a5e9959605a1b6044820152606490fd5b507fa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d10154156128bf565b346103cc5760e03660031901126103cc576129fa61410e565b612a02614124565b90612a0b614150565b9161ffff60643516606435036103cc5763ffffffff60843516608435036103cc5760a4356001600160401b0381116103cc57366023820112156103cc57612a5c90369060248160040135910161429f565b9260c4356001600160401b0381116103cc57366023820112156103cc57612a8d90369060248160040135910161432d565b60008051602061569583398151915254936001600160401b03851680159081613022575b6001149081613018575b15908161300f575b50612ffd5760016001600160401b03198616176000805160206156958339815191525560ff8560401c1615612fd0575b612afb6150f1565b612b036150f1565b6001600160a01b03811615612fb757612b1b90614943565b60405194612b288661424c565b600e86526d13195d1d195c93d990dc99591a5d60921b602087015260405195612b508761424c565b60018752603160f81b6020880152612b666150f1565b612b6e6150f1565b8051906001600160401b038211611a35578190612b996000805160206155f583398151915254614b6f565b601f8111612f36575b50602090601f8311600114612ea457600092612e99575b50508160011b916000199060031b1c1916176000805160206155f5833981519152555b85516001600160401b038111611a3557612c0460008051602061563583398151915254614b6f565b601f8111612e23575b506020601f8211600114612d8a57918161287e92612cc3959460ff9a600092612d7f575b50508160011b916000199060031b1c191617600080516020615635833981519152555b60007fa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d1005560007fa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d10155612ca46150f1565b612cac6150f1565b60016000805160206156758339815191525561485d565b600180546001600160a01b03199081166001600160a01b039485161790915560028054909116919092161790556000805465ffffffffffff60601b191660843560701b63ffffffff60701b161760643560601b61ffff60601b1617905560401c1615612d2b57005b60ff60401b196000805160206156958339815191525416600080516020615695833981519152557fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2602060405160018152a1005b015190508a80612c31565b6000805160206156358339815191526000526000805160206156d58339815191529760005b601f1984168110612e0b57508260ff99612cc396959360019361287e96601f19811610612df2575b505050811b0160008051602061563583398151915255612c54565b015160001960f88460031b161c191690558a8080612dd7565b828201518a5560019099019860209283019201612daf565b600080516020615635833981519152600052601f820160051c6000805160206156d58339815191520160208310612e84575b601f820160051c6000805160206156d5833981519152018110612e785750612c0d565b60008155600101612e55565b506000805160206156d5833981519152612e55565b015190508880612bb9565b92506000805160206155f5833981519152600052600080516020615655833981519152906000935b601f1984168510612f1b576001945083601f19811610612f02575b505050811b016000805160206155f583398151915255612bdc565b015160001960f88460031b161c19169055888080612ee7565b81810151835560209485019460019093019290910190612ecc565b9091506000805160206155f5833981519152600052601f830160051c6000805160206156558339815191520160208410612fa2575b908392915b601f820160051c600080516020615655833981519152018110612f935750612ba2565b60008155849350600101612f70565b50600080516020615655833981519152612f6b565b604051631e4fbdf760e01b815260006004820152602490fd5b68ffffffffffffffffff198516680100000000000000011760008051602061569583398151915255612af3565b60405163f92ee8a960e01b8152600490fd5b90501587612ac3565b303b159150612abb565b604087901c60ff16159150612ab1565b346103cc5760203660031901126103cc57602460207f044dcf2257e109d3359aa04668eaf3065abf983bf9a013395d4edab6633c023661307061410e565b6130786147df565b600154604080516001600160a01b0380841682529390931660208401819052938493909190a16001600160a01b031916176001556040516301ffc9a760e01b81526316e15aeb60e21b600482015292839182905afa600091816130ea575b506130e357610bc5614507565b15610c2057005b61310491925060203d602011610c5f57610c508183614267565b90826130d6565b346103cc5760003660031901126103cc576000805160206156b583398151915254336001600160a01b03909116036131465761288333614943565b60405163118cdaa760e01b8152336004820152602490fd5b346103cc5760003660031901126103cc5760206040517fb2c9dd2d7f83ecfa5c4f5d7679b1f3acfbb0d53f15dccf8b1524979826eaa6c78152f35b346103cc5760003660031901126103cc576131b26147df565b6000805160206156b583398151915280546001600160a01b0319908116909155600080516020615615833981519152805491821690556000906001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b346103cc5760203660031901126103cc576004356001600160401b038082116103cc57366023830112156103cc5781600401359081116103cc573660248260071b840101116103cc576128839161327d916132736147df565b602436920161429f565b61485d565b60a03660031901126103cc576132966141a7565b61329e614124565b906001600160401b03906044358281116103cc576132c090369060040161417a565b9190926064358181116103cc576132db90369060040161417a565b50506084359081116103cc576132f590369060040161417a565b93909247946133026145f4565b6001600160601b0384166000526003602052604060002094600760405196613329886141cc565b80546001600160601b038116895260601c6020890152600181015460018060a01b03811660408a015263ffffffff8160a01c1660608a015261ffff90818160c01c1660808b015260d01c1660a089015260018060a01b0360028201541660c089015260018060a01b0360038201541660e08901526004810154610100890152600581015461012089015260018060a01b0360068201541661014089015201548061016088015215613a295760e08601516101408701516001600160a01b03908116911614613a085763ffffffff606087015116428111156139d9575061016086015160e08701516101408801516001600160a01b03918216969116946000949261344292909161343b91369190614468565b9089614e0d565b94608086015192865115806139d1575b6139b0576080870151801580156139a4575b61398257506001600160a01b038b161561386a5760c0890151895160408051637500616d60e01b81526001600160601b03909216600483015260248201879052306044830152600160648301528d92829060849082906000906001600160a01b03165af180156106f8576134e69160009161384a575b506101008c01516144e2565b5060405163095ea7b360e01b60208201526001600160a01b03821660248201526044810186905261352481606481015b03601f198101835282614267565b60008082516020840182885af1613539614507565b8161381b575b5080613811575b156137cd575b506040516370a0823160e01b8152306004820152946020866024818b5afa9586156106f857600096613799575b506020890151926001600160a01b0383163b156103cc576000946135d88692604051988997889687956335d090f360e11b8752336004880152602487015260448601528d6064860152608485015260c060a485015260c4840191614594565b03926001600160a01b03165af180156106f857613781575b50602060249293604051938480926370a0823160e01b82523060048301525afa801561165257839061374d575b61362792506144e2565b6020830151908181036116005750506106ce957f21640bc6d24e28d13a51d024a2c86f8dd94df3889ab646059044fad4cf4eded4915b6001600160601b038516815260036020526136f86003604080842093610160602089019a8b51806004890155600788015560018060a01b0361014082015116966002868201916001600160601b0360a01b928a848254161790558560058201556001810163ffffffff60c01b1981541690556001600160601b03198154168155019081541690550151948152600460205220019182546144e2565b9055604083810151606094850151965182519182526020820197909752908101959095526001600160a01b03169333936001600160601b03169290a460016000805160206156758339815191525534906144e2565b506020823d602011613779575b8161376760209383614267565b810103126103cc57613627915161361d565b3d915061375a565b6024925061378e9061421e565b6020600092506135f0565b9095506020813d6020116137c5575b816137b560209383614267565b810103126103cc5751948d613579565b3d91506137a8565b60405163095ea7b360e01b60208201526001600160a01b03831660248201526000604480830191909152815261380b91906116e1906116db816141e8565b8c61354c565b50833b1515613546565b8051801592508215613830575b50508e61353f565b61384392506020809183010191016144ef565b8e80613828565b613863915060403d6040116109705761095e8183614267565b508e6134da565b5050506138d59750604090929192339233613943575b5060c087015187518351637500616d60e01b81526001600160601b039091166004820152602481019290925233604483015260016064830152909889916001600160a01b031690829060009082906084820190565b03925af19182156106f85761391d7f21640bc6d24e28d13a51d024a2c86f8dd94df3889ab646059044fad4cf4eded4936106ce99600091613923575b506101008801516144e2565b5061365d565b61393c915060403d6040116109705761095e8183614267565b508a613911565b60208681015184516323b872dd60e01b92810192909252336024830152306044830152606482015261397c916118358260848101611827565b89613880565b60449060208901516040519163c661aab760e01b835260048301526024820152fd5b50602088015115613464565b604051639df5f2d760e01b81526001600160601b0389166004820152602490fd5b506001613452565b604051632cbdae0760e01b81526001600160601b038716600482015263ffffffff919091166024820152604490fd5b6040516332f1c16d60e11b81526001600160601b0386166004820152602490fd5b60405163810fbf1f60e01b81526001600160601b0386166004820152602490fd5b346103cc5760203660031901126103cc57613a6361410e565b613a6b6144bd565b5060018060a01b0316600052600460205260806040600020604051613a8f816141e8565b815491828252600181015460208301908152606060036002840154936040860194855201549301928352604051938452516020840152516040830152516060820152f35b346103cc5760003660031901126103cc576002546040516001600160a01b039091168152602090f35b346103cc5760203660031901126103cc5763ffffffff60043581811681036103cc57613b266147df565b60005460408051607083901c9490941663ffffffff90811685528316602085015290927faf14225344892ccd1d66a398b930d5d60e40729143c4f106f16f62df6a3090ac9190a163ffffffff60701b1990911660709190911b63ffffffff60701b1617600055005b346103cc5760403660031901126103cc57613ba761410e565b613baf614124565b90613bb86144bd565b5060018060a01b038091166000526005602052604060002091166000526020526080604060002060405190613bec826141e8565b5461ffff9081808216938481528160208201818560101c1681528160606040850194828860201c168652019560301c168552604051968752511660208601525116604084015251166060820152f35b346103cc5760c03660031901126103cc57613c5461410e565b613c5c614124565b63ffffffff60643516606435036103cc576001600160401b03916084358381116103cc57613c8e90369060040161417a565b91909360a4359081116103cc57613ca990369060040161417a565b949092613cb46145f4565b6102008087116140f0575080614047575b50504263ffffffff60643516111561402057613ce94263ffffffff606435166144e2565b63ffffffff60005460701c16809111613ff65750600154604080516307e8761360e41b81523360048201526001600160a01b038681166024830152604480359083015290969216929190866064816000875af19182156106f857602096600090600094613fae575b50613f88907f6b401e682129f6a19f416a78c37fd6d263bfc32f134438588451bfbbb7b9baed949560005490613d8f6001600160601b038316614755565b998a926001600160601b038416906001600160601b03191617600055604051613db7816141cc565b6001600160601b0385168152338d82015260018060a01b038716604082015263ffffffff60643516606082015260006080820152600060a08201528260c082015260018060a01b03821660e08201528961010082015260443561012082015260018060a01b0382166101408201526044356101608201526001600160601b03841660005260038d5260076101606040600020926001600160601b038151166001600160601b0319602083015160601b1617845560018401613f0460018060a01b03604084015116916001600160601b0360a01b9283825416178155613ec263ffffffff606086015116829081549063ffffffff60a01b9060a01b169063ffffffff60a01b1916179055565b6080840151815461ffff60c01b191660c09190911b61ffff60c01b1617815561ffff60a0850151825461ffff60d01b1916911660d01b61ffff60d01b16179055565b60c082015160028601805483166001600160a01b0392831617905560e0830151600387018054841691831691909117905561010083015160048701556101208301516005870155610140830151600687018054909316908216179091559101519190920155604051961699339996978897604435946064359386938692918c61476d565b0390a36001600080516020615675833981519152556001600160601b0360405191168152f35b7f6b401e682129f6a19f416a78c37fd6d263bfc32f134438588451bfbbb7b9baed9450613f889150613fee9060403d60401161237e5761236e8183614267565b945090613d51565b60405163cf113d3160e01b815263ffffffff91821660048201526064359091166024820152604490fd5b604051632cbdae0760e01b81526000600482015260643563ffffffff166024820152604490fd5b6001546040516250fd9760e21b815292906001600160a01b0316602084600481845afa9384156106f85787906000956140ad575b5061252c6140a161271061409861ffff6140a69916604435614d65565b04604435614624565b614631565b8480613cc5565b919450506020813d6020116140e8575b816140ca60209383614267565b810103126103cc57519261ffff841684036103cc578661252c61407b565b3d91506140bd565b604490876040519163b2560b7960e01b835260048301526024820152fd5b600435906001600160a01b03821682036103cc57565b602435906001600160a01b03821682036103cc57565b606435906001600160a01b03821682036103cc57565b604435906001600160a01b03821682036103cc57565b35906001600160a01b03821682036103cc57565b9181601f840112156103cc578235916001600160401b0383116103cc57602083818601950101116103cc57565b600435906001600160601b03821682036103cc57565b359061ffff821682036103cc57565b61018081019081106001600160401b03821117611a3557604052565b608081019081106001600160401b03821117611a3557604052565b606081019081106001600160401b03821117611a3557604052565b6001600160401b038111611a3557604052565b60a081019081106001600160401b03821117611a3557604052565b604081019081106001600160401b03821117611a3557604052565b90601f801991011681019081106001600160401b03821117611a3557604052565b6001600160401b038111611a355760051b60200190565b9291926142ab82614288565b6040926142bb6040519283614267565b819581835260208093019160071b8401938185116103cc57915b8483106142e457505050505050565b6080838303126103cc578360809187516142fd816141e8565b61430686614166565b815282860135838201528886013589820152606080870135908201528152019201916142d5565b92919261433982614288565b60409461434886519283614267565b819584835260208093019160c08096028501948186116103cc57925b8584106143745750505050505050565b8382038781126103cc5783519161438a83614203565b61439386614166565b83526143a0878701614166565b8784015260808092603f1901126103cc57889287928651906143c1826141e8565b6143cc888a016141bd565b82526143ea6060916143df838c016141bd565b878501528a016141bd565b888301526143fa60a08a016141bd565b9082015286820152815201930192614364565b919082519283825260005b848110614439575050826000602080949584010152601f8019910116010190565b602081830181015184830182015201614418565b6001600160401b038111611a3557601f01601f191660200190565b9291926144748261444d565b916144826040519384614267565b8294818452818301116103cc578281602093846000960137010152565b9080601f830112156103cc578160206144ba93359101614468565b90565b604051906144ca826141e8565b60006060838281528260208201528260408201520152565b9190820391821161092a57565b908160209103126103cc575180151581036103cc5790565b3d15614532573d906145188261444d565b916145266040519384614267565b82523d6000602084013e565b606090565b6040519061454482614203565b60006040838281528260208201520152565b908160609103126103cc576040519061456e82614203565b805182526020810151908160030b82036103cc5760409160208401520151604082015290565b908060209392818452848401376000828201840152601f01601f1916010190565b6001600160a01b039182168152911660208201526060604082018190526144ba93910191614594565b91908260409103126103cc576020825192015190565b60008051602061567583398151915260028154146146125760029055565b604051633ee5aeb560e01b8152600490fd5b9190820180921161092a57565b6001600160ff1b0381116146425790565b6024906040519063ddc5fd2560e01b82526004820152fd5b90936001600160a01b03929091831690813b156103cc57600080946146b66040519788968795869463147c0f9b60e31b8652869c336004880152306024880152166044860152606485015260a0608485015260a4840191614594565b03925af1908161471a575b50614717576146ce614507565b9050602081519101805163ffffffff60e01b8082169160048510614702575b50166309ea8ec360e21b01614700575050565bfd5b818093508560040360031b1b161690386146ed565b50565b61472591925061421e565b600090386146c1565b91908260409103126103cc5781516001600160601b03811681036103cc5760209092015190565b6001600160601b0380911690811461092a5760010190565b9997939b9a96919463ffffffff936147966101209b98936020988e610140908181520191614594565b9d8c60018060a01b0398898094169101521660408c015260608b015260808a01521660a08801521660c086015260e08501526001600160601b0380921661010085015216910152565b600080516020615615833981519152546001600160a01b0316330361314657565b6001600160601b03909116815260208101919091526001600160a01b039091166040820152901515606082015260800190565b80518210156148475760209160051b010190565b634e487b7160e01b600052603260045260246000fd5b9060009160005b815181101561493d576148778183614833565b51602080820151604091828401519288606060018060a01b038751169601519283614904575b92828160027fe8717d5a73c7565350b82e71c4d5e4e746eaded98792418833b20ca1a5a8c67597958560048c9b9960019e9d6148fb99525220868155878c820155015551938493846040919493926060820195825260208201520152565b0390a201614864565b90508484101561492c57831561491b57899061489d565b81516377fb84ef60e01b8152600490fd5b8151630ff84e1560e31b8152600490fd5b50509050565b6000805160206156b583398151915280546001600160a01b031990811690915560008051602061561583398151915280549182166001600160a01b0393841690811790915591167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a3565b60005b8151811015614b6b576149c78183614833565b5160409081810151918083019261ffff938481511661271093848211614b54578684511694808611614b3c57602080860195898751169288841115614b1957839003968a881161092a578a881697888711614af557606097888401918d8351169a8b11614ad157508b85600160a01b60019003809251169d0151169c8c600052600586528d88600020906000528652876000209451168454935160101b63ffff0000169151861b65ffff0000000016925160301b67ffff00000000000016936001600160401b031916171717179055825196875286015284015282015260807f0fc3464bbc911b6f91c84b623aad53d301ba43fd3970579a4d57963d9b71797d91a36001016149b4565b87516311c78d9360e01b815261ffff9182166004820152908b166024820152604490fd5b855163118466cf60e21b815261ffff91821660048201529087166024820152604490fd5b845163ba743e6d60e01b815261ffff8a8116600483015285166024820152604490fd5b8151633b21b52f60e11b815260048101879052602490fd5b602492505190633b21b52f60e11b82526004820152fd5b5050565b90600182811c92168015614b9f575b6020831014614b8957565b634e487b7160e01b600052602260045260246000fd5b91607f1691614b7e565b604051906000826000805160206155f583398151915291825492614bcc84614b6f565b80845293602091600191828116908115614c495750600114614bfa575b505050614bf892500383614267565b565b60009081526000805160206156558339815191529590935091905b828410614c315750614bf8945050508101602001388080614be9565b85548885018301529485019487945092810192614c15565b9250505060209250614bf894915060ff191682840152151560051b820101388080614be9565b6040519060008260008051602061563583398151915291825492614c9284614b6f565b80845293602091600191828116908115614c495750600114614cbd57505050614bf892500383614267565b60009081526000805160206156d58339815191529590935091905b828410614cf45750614bf8945050508101602001388080614be9565b85548885018301529485019487945092810192614cd8565b63ffffffff164281111580614d4a575b614d235750565b60449061ffff60005460601c166040519163196172bf60e11b835260048301526024820152fd5b50614d5581426144e2565b61ffff60005460601c1610614d1c565b8181029291811591840414171561092a57565b8115614d82570490565b634e487b7160e01b600052601260045260246000fd5b91614da391926152d5565b8015614dda576127109182810292818404149015171561092a57614dc691614d78565b61ffff9081811115614dd6575090565b1690565b505061ffff90565b60405190614def82614231565b60006080838281528260208201528260408201528260608201520152565b91614e16614de2565b506000614e21614537565b5081511561506e575060025460e084015161014085015160405163b37b327160e01b81526001600160a01b0392831660048201529082166024820152606060448201819052909390928492169082908190614e8090606483019061440d565b039134905af19081156106f85760009161504f575b505b604090614eac63ffffffff8383015116614d0c565b60018060a01b038060e08601511660005260056020528260002090610140860151166000526020528160002091805192614ee5846141e8565b549361ffff9586861685526020850195878160101c16875287606085880197828460201c168952019160301c168152610120820196614f258689516152d5565b801561503e57614f39908a8951169061533d565b91610160840192835180821061500d575050614f5b61010085019788516152d5565b9251612710928382029180830485149015171561092a5784614fb29388614faa938f614f89819a8298614d78565b9151161180159f9061500157614fa592508d51169b5b51614d65565b614d78565b971687614d65565b0495614fbe8787614624565b97518811614fec57505050815195614fd587614231565b865260208601528401526060830152608082015290565b93509350945092506144ba945051169161536b565b614fa59251169b614f9f565b9594995097959a9996505050500361502d576144ba94955051169161536b565b85516384cd6fa160e01b8152600490fd5b85516342486e5160e01b8152600490fd5b615068915060603d60601161084d5761083f8183614267565b38614e95565b60025460e0850151610140860151604051635620c32d60e11b81526001600160a01b0392831660048201529082166024820152929350606091839160449183918791165af19182156150e557916150c6575b50614e97565b6150df915060603d60601161084d5761083f8183614267565b386150c0565b604051903d90823e3d90fd5b60ff6000805160206156958339815191525460401c161561510e57565b604051631afcd79f60e31b8152600490fd5b6151286154e2565b61513061554c565b916040519260208401927f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f8452604085015260608401524660808401523060a084015260a0835260c08301918383106001600160401b03841117611a355760429360e291846040528151902061190160f01b855260c282015201522090565b6151b9838361542c565b50600481959295101561527857159384615262575b5083156151dc575b50505090565b6000929350908291604051615215816135166020820194630b135d3f60e11b998a8752602484015260406044840152606483019061440d565b51915afa90615222614507565b82615254575b82615238575b50503880806151d6565b9091506020818051810103126103cc576020015114388061522e565b915060208251101591615228565b6001600160a01b038381169116149350386151ce565b634e487b7160e01b600052602160045260246000fd5b60405163a9059cbb60e01b60208201526001600160a01b0390921660248301526044820192909252614bf8916118358260648101611827565b604d811161092a57600a0a90565b60208201906000825160030b1260001461531b576152f591925190614d65565b905160030b600003908160030b91820361092a576153156144ba926152c7565b90614d78565b61532d615337916144ba945190614d65565b915160030b6152c7565b90614d65565b6127108082029180830482149015171561092a5761ffff809316019180831161092a576144ba921690614d78565b6101206153879193929361537d614de2565b500151918261533d565b61539181836144e2565b906040519361539f85614231565b60018552602085015260408401526060830152608082015290565b6000806153e39260018060a01b03169360208151910182865af16153dc614507565b9083615591565b8051908115159182615411575b50506153f95750565b60249060405190635274afe760e01b82526004820152fd5b61542492506020809183010191016144ef565b1538806153f0565b815191906041830361545d5761545692506020820151906060604084015193015160001a90615468565b9192909190565b505060009160029190565b91906fa2a8918ca85bafe22016d0b997e4df60600160ff1b0384116154d657926020929160ff608095604051948552168484015260408301526060820152600092839182805260015afa156150e55780516001600160a01b038116156154cd57918190565b50809160019190565b50505060009160039190565b6154ea614ba9565b80519081156154fa576020012090565b50507fa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d1005480156155275790565b507fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47090565b615554614c6f565b8051908115615564576020012090565b50507fa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d1015480156155275790565b906155b857508051156155a657805190602001fd5b604051630a12f52160e11b8152600490fd5b815115806155eb575b6155c9575090565b604051639996b31560e01b81526001600160a01b039091166004820152602490fd5b50803b156155c156fea16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d1029016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300a16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d10342ad5d3e1f2e6e70edcf6d991b8a3023d3fca8047a131592f9edb9fd9b89d57d9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00f0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00237e158222e3e6968b72b9db0d8043aacf074ad9f650f0d1606b4d82ee432c005f9ce34815f8e11431c7bb75a8e6886a91478f7ffc1dbb0a98dc240fddd76b75a26469706673582212200f82a9e805f4c7bce863baa2d530c7baf1159d9af2e4175da16e1ef76042d6de64736f6c63430008190033","name":"LetterOfCredit","is_blueprint":false,"license_type":"none","is_fully_verified":true,"is_verified_via_eth_bytecode_db":false,"language":"solidity","evm_version":"paris","can_be_visualized_via_sol2uml":true,"is_verified_via_sourcify":false,"additional_sources":[{"file_path":"@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable2Step.sol)\n\npragma solidity ^0.8.20;\n\nimport {OwnableUpgradeable} from \"./OwnableUpgradeable.sol\";\nimport {Initializable} from \"../proxy/utils/Initializable.sol\";\n\n/**\n * @dev Contract module which provides access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * The initial owner is specified at deployment time in the constructor for `Ownable`. This\n * can later be changed with {transferOwnership} and {acceptOwnership}.\n *\n * This module is used through inheritance. It will make available all functions\n * from parent (Ownable).\n */\nabstract contract Ownable2StepUpgradeable is Initializable, OwnableUpgradeable {\n    /// @custom:storage-location erc7201:openzeppelin.storage.Ownable2Step\n    struct Ownable2StepStorage {\n        address _pendingOwner;\n    }\n\n    // keccak256(abi.encode(uint256(keccak256(\"openzeppelin.storage.Ownable2Step\")) - 1)) & ~bytes32(uint256(0xff))\n    bytes32 private constant Ownable2StepStorageLocation = 0x237e158222e3e6968b72b9db0d8043aacf074ad9f650f0d1606b4d82ee432c00;\n\n    function _getOwnable2StepStorage() private pure returns (Ownable2StepStorage storage $) {\n        assembly {\n            $.slot := Ownable2StepStorageLocation\n        }\n    }\n\n    event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);\n\n    function __Ownable2Step_init() internal onlyInitializing {\n    }\n\n    function __Ownable2Step_init_unchained() internal onlyInitializing {\n    }\n    /**\n     * @dev Returns the address of the pending owner.\n     */\n    function pendingOwner() public view virtual returns (address) {\n        Ownable2StepStorage storage $ = _getOwnable2StepStorage();\n        return $._pendingOwner;\n    }\n\n    /**\n     * @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.\n     * Can only be called by the current owner.\n     */\n    function transferOwnership(address newOwner) public virtual override onlyOwner {\n        Ownable2StepStorage storage $ = _getOwnable2StepStorage();\n        $._pendingOwner = newOwner;\n        emit OwnershipTransferStarted(owner(), newOwner);\n    }\n\n    /**\n     * @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.\n     * Internal function without access restriction.\n     */\n    function _transferOwnership(address newOwner) internal virtual override {\n        Ownable2StepStorage storage $ = _getOwnable2StepStorage();\n        delete $._pendingOwner;\n        super._transferOwnership(newOwner);\n    }\n\n    /**\n     * @dev The new owner accepts the ownership transfer.\n     */\n    function acceptOwnership() public virtual {\n        address sender = _msgSender();\n        if (pendingOwner() != sender) {\n            revert OwnableUnauthorizedAccount(sender);\n        }\n        _transferOwnership(sender);\n    }\n}\n"},{"file_path":"@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)\n\npragma solidity ^0.8.20;\n\nimport {ContextUpgradeable} from \"../utils/ContextUpgradeable.sol\";\nimport {Initializable} from \"../proxy/utils/Initializable.sol\";\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * The initial owner is set to the address provided by the deployer. This can\n * later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {\n    /// @custom:storage-location erc7201:openzeppelin.storage.Ownable\n    struct OwnableStorage {\n        address _owner;\n    }\n\n    // keccak256(abi.encode(uint256(keccak256(\"openzeppelin.storage.Ownable\")) - 1)) & ~bytes32(uint256(0xff))\n    bytes32 private constant OwnableStorageLocation = 0x9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300;\n\n    function _getOwnableStorage() private pure returns (OwnableStorage storage $) {\n        assembly {\n            $.slot := OwnableStorageLocation\n        }\n    }\n\n    /**\n     * @dev The caller account is not authorized to perform an operation.\n     */\n    error OwnableUnauthorizedAccount(address account);\n\n    /**\n     * @dev The owner is not a valid owner account. (eg. `address(0)`)\n     */\n    error OwnableInvalidOwner(address owner);\n\n    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n    /**\n     * @dev Initializes the contract setting the address provided by the deployer as the initial owner.\n     */\n    function __Ownable_init(address initialOwner) internal onlyInitializing {\n        __Ownable_init_unchained(initialOwner);\n    }\n\n    function __Ownable_init_unchained(address initialOwner) internal onlyInitializing {\n        if (initialOwner == address(0)) {\n            revert OwnableInvalidOwner(address(0));\n        }\n        _transferOwnership(initialOwner);\n    }\n\n    /**\n     * @dev Throws if called by any account other than the owner.\n     */\n    modifier onlyOwner() {\n        _checkOwner();\n        _;\n    }\n\n    /**\n     * @dev Returns the address of the current owner.\n     */\n    function owner() public view virtual returns (address) {\n        OwnableStorage storage $ = _getOwnableStorage();\n        return $._owner;\n    }\n\n    /**\n     * @dev Throws if the sender is not the owner.\n     */\n    function _checkOwner() internal view virtual {\n        if (owner() != _msgSender()) {\n            revert OwnableUnauthorizedAccount(_msgSender());\n        }\n    }\n\n    /**\n     * @dev Leaves the contract without owner. It will not be possible to call\n     * `onlyOwner` functions. Can only be called by the current owner.\n     *\n     * NOTE: Renouncing ownership will leave the contract without an owner,\n     * thereby disabling any functionality that is only available to the owner.\n     */\n    function renounceOwnership() public virtual onlyOwner {\n        _transferOwnership(address(0));\n    }\n\n    /**\n     * @dev Transfers ownership of the contract to a new account (`newOwner`).\n     * Can only be called by the current owner.\n     */\n    function transferOwnership(address newOwner) public virtual onlyOwner {\n        if (newOwner == address(0)) {\n            revert OwnableInvalidOwner(address(0));\n        }\n        _transferOwnership(newOwner);\n    }\n\n    /**\n     * @dev Transfers ownership of the contract to a new account (`newOwner`).\n     * Internal function without access restriction.\n     */\n    function _transferOwnership(address newOwner) internal virtual {\n        OwnableStorage storage $ = _getOwnableStorage();\n        address oldOwner = $._owner;\n        $._owner = newOwner;\n        emit OwnershipTransferred(oldOwner, newOwner);\n    }\n}\n"},{"file_path":"@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed\n * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an\n * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer\n * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.\n *\n * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be\n * reused. This mechanism prevents re-execution of each \"step\" but allows the creation of new initialization steps in\n * case an upgrade adds a module that needs to be initialized.\n *\n * For example:\n *\n * [.hljs-theme-light.nopadding]\n * ```solidity\n * contract MyToken is ERC20Upgradeable {\n *     function initialize() initializer public {\n *         __ERC20_init(\"MyToken\", \"MTK\");\n *     }\n * }\n *\n * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {\n *     function initializeV2() reinitializer(2) public {\n *         __ERC20Permit_init(\"MyToken\");\n *     }\n * }\n * ```\n *\n * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as\n * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.\n *\n * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure\n * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.\n *\n * [CAUTION]\n * ====\n * Avoid leaving a contract uninitialized.\n *\n * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation\n * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke\n * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:\n *\n * [.hljs-theme-light.nopadding]\n * ```\n * /// @custom:oz-upgrades-unsafe-allow constructor\n * constructor() {\n *     _disableInitializers();\n * }\n * ```\n * ====\n */\nabstract contract Initializable {\n    /**\n     * @dev Storage of the initializable contract.\n     *\n     * It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions\n     * when using with upgradeable contracts.\n     *\n     * @custom:storage-location erc7201:openzeppelin.storage.Initializable\n     */\n    struct InitializableStorage {\n        /**\n         * @dev Indicates that the contract has been initialized.\n         */\n        uint64 _initialized;\n        /**\n         * @dev Indicates that the contract is in the process of being initialized.\n         */\n        bool _initializing;\n    }\n\n    // keccak256(abi.encode(uint256(keccak256(\"openzeppelin.storage.Initializable\")) - 1)) & ~bytes32(uint256(0xff))\n    bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;\n\n    /**\n     * @dev The contract is already initialized.\n     */\n    error InvalidInitialization();\n\n    /**\n     * @dev The contract is not initializing.\n     */\n    error NotInitializing();\n\n    /**\n     * @dev Triggered when the contract has been initialized or reinitialized.\n     */\n    event Initialized(uint64 version);\n\n    /**\n     * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,\n     * `onlyInitializing` functions can be used to initialize parent contracts.\n     *\n     * Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any\n     * number of times. This behavior in the constructor can be useful during testing and is not expected to be used in\n     * production.\n     *\n     * Emits an {Initialized} event.\n     */\n    modifier initializer() {\n        // solhint-disable-next-line var-name-mixedcase\n        InitializableStorage storage $ = _getInitializableStorage();\n\n        // Cache values to avoid duplicated sloads\n        bool isTopLevelCall = !$._initializing;\n        uint64 initialized = $._initialized;\n\n        // Allowed calls:\n        // - initialSetup: the contract is not in the initializing state and no previous version was\n        //                 initialized\n        // - construction: the contract is initialized at version 1 (no reininitialization) and the\n        //                 current contract is just being deployed\n        bool initialSetup = initialized == 0 && isTopLevelCall;\n        bool construction = initialized == 1 && address(this).code.length == 0;\n\n        if (!initialSetup && !construction) {\n            revert InvalidInitialization();\n        }\n        $._initialized = 1;\n        if (isTopLevelCall) {\n            $._initializing = true;\n        }\n        _;\n        if (isTopLevelCall) {\n            $._initializing = false;\n            emit Initialized(1);\n        }\n    }\n\n    /**\n     * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the\n     * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be\n     * used to initialize parent contracts.\n     *\n     * A reinitializer may be used after the original initialization step. This is essential to configure modules that\n     * are added through upgrades and that require initialization.\n     *\n     * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`\n     * cannot be nested. If one is invoked in the context of another, execution will revert.\n     *\n     * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in\n     * a contract, executing them in the right order is up to the developer or operator.\n     *\n     * WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.\n     *\n     * Emits an {Initialized} event.\n     */\n    modifier reinitializer(uint64 version) {\n        // solhint-disable-next-line var-name-mixedcase\n        InitializableStorage storage $ = _getInitializableStorage();\n\n        if ($._initializing || $._initialized >= version) {\n            revert InvalidInitialization();\n        }\n        $._initialized = version;\n        $._initializing = true;\n        _;\n        $._initializing = false;\n        emit Initialized(version);\n    }\n\n    /**\n     * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the\n     * {initializer} and {reinitializer} modifiers, directly or indirectly.\n     */\n    modifier onlyInitializing() {\n        _checkInitializing();\n        _;\n    }\n\n    /**\n     * @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.\n     */\n    function _checkInitializing() internal view virtual {\n        if (!_isInitializing()) {\n            revert NotInitializing();\n        }\n    }\n\n    /**\n     * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.\n     * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized\n     * to any version. It is recommended to use this to lock implementation contracts that are designed to be called\n     * through proxies.\n     *\n     * Emits an {Initialized} event the first time it is successfully executed.\n     */\n    function _disableInitializers() internal virtual {\n        // solhint-disable-next-line var-name-mixedcase\n        InitializableStorage storage $ = _getInitializableStorage();\n\n        if ($._initializing) {\n            revert InvalidInitialization();\n        }\n        if ($._initialized != type(uint64).max) {\n            $._initialized = type(uint64).max;\n            emit Initialized(type(uint64).max);\n        }\n    }\n\n    /**\n     * @dev Returns the highest version that has been initialized. See {reinitializer}.\n     */\n    function _getInitializedVersion() internal view returns (uint64) {\n        return _getInitializableStorage()._initialized;\n    }\n\n    /**\n     * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.\n     */\n    function _isInitializing() internal view returns (bool) {\n        return _getInitializableStorage()._initializing;\n    }\n\n    /**\n     * @dev Returns a pointer to the storage namespace.\n     */\n    // solhint-disable-next-line var-name-mixedcase\n    function _getInitializableStorage() private pure returns (InitializableStorage storage $) {\n        assembly {\n            $.slot := INITIALIZABLE_STORAGE\n        }\n    }\n}\n"},{"file_path":"@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)\n\npragma solidity ^0.8.20;\nimport {Initializable} from \"../proxy/utils/Initializable.sol\";\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract ContextUpgradeable is Initializable {\n    function __Context_init() internal onlyInitializing {\n    }\n\n    function __Context_init_unchained() internal onlyInitializing {\n    }\n    function _msgSender() internal view virtual returns (address) {\n        return msg.sender;\n    }\n\n    function _msgData() internal view virtual returns (bytes calldata) {\n        return msg.data;\n    }\n\n    function _contextSuffixLength() internal view virtual returns (uint256) {\n        return 0;\n    }\n}\n"},{"file_path":"@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol)\n\npragma solidity ^0.8.20;\nimport {Initializable} from \"../proxy/utils/Initializable.sol\";\n\n/**\n * @dev Contract module that helps prevent reentrant calls to a function.\n *\n * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier\n * available, which can be applied to functions to make sure there are no nested\n * (reentrant) calls to them.\n *\n * Note that because there is a single `nonReentrant` guard, functions marked as\n * `nonReentrant` may not call one another. This can be worked around by making\n * those functions `private`, and then adding `external` `nonReentrant` entry\n * points to them.\n *\n * TIP: If you would like to learn more about reentrancy and alternative ways\n * to protect against it, check out our blog post\n * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].\n */\nabstract contract ReentrancyGuardUpgradeable is Initializable {\n    // Booleans are more expensive than uint256 or any type that takes up a full\n    // word because each write operation emits an extra SLOAD to first read the\n    // slot's contents, replace the bits taken up by the boolean, and then write\n    // back. This is the compiler's defense against contract upgrades and\n    // pointer aliasing, and it cannot be disabled.\n\n    // The values being non-zero value makes deployment a bit more expensive,\n    // but in exchange the refund on every call to nonReentrant will be lower in\n    // amount. Since refunds are capped to a percentage of the total\n    // transaction's gas, it is best to keep them low in cases like this one, to\n    // increase the likelihood of the full refund coming into effect.\n    uint256 private constant NOT_ENTERED = 1;\n    uint256 private constant ENTERED = 2;\n\n    /// @custom:storage-location erc7201:openzeppelin.storage.ReentrancyGuard\n    struct ReentrancyGuardStorage {\n        uint256 _status;\n    }\n\n    // keccak256(abi.encode(uint256(keccak256(\"openzeppelin.storage.ReentrancyGuard\")) - 1)) & ~bytes32(uint256(0xff))\n    bytes32 private constant ReentrancyGuardStorageLocation = 0x9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00;\n\n    function _getReentrancyGuardStorage() private pure returns (ReentrancyGuardStorage storage $) {\n        assembly {\n            $.slot := ReentrancyGuardStorageLocation\n        }\n    }\n\n    /**\n     * @dev Unauthorized reentrant call.\n     */\n    error ReentrancyGuardReentrantCall();\n\n    function __ReentrancyGuard_init() internal onlyInitializing {\n        __ReentrancyGuard_init_unchained();\n    }\n\n    function __ReentrancyGuard_init_unchained() internal onlyInitializing {\n        ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();\n        $._status = NOT_ENTERED;\n    }\n\n    /**\n     * @dev Prevents a contract from calling itself, directly or indirectly.\n     * Calling a `nonReentrant` function from another `nonReentrant`\n     * function is not supported. It is possible to prevent this from happening\n     * by making the `nonReentrant` function external, and making it call a\n     * `private` function that does the actual work.\n     */\n    modifier nonReentrant() {\n        _nonReentrantBefore();\n        _;\n        _nonReentrantAfter();\n    }\n\n    function _nonReentrantBefore() private {\n        ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();\n        // On the first call to nonReentrant, _status will be NOT_ENTERED\n        if ($._status == ENTERED) {\n            revert ReentrancyGuardReentrantCall();\n        }\n\n        // Any calls to nonReentrant after this point will fail\n        $._status = ENTERED;\n    }\n\n    function _nonReentrantAfter() private {\n        ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();\n        // By storing the original value once again, a refund is triggered (see\n        // https://eips.ethereum.org/EIPS/eip-2200)\n        $._status = NOT_ENTERED;\n    }\n\n    /**\n     * @dev Returns true if the reentrancy guard is currently set to \"entered\", which indicates there is a\n     * `nonReentrant` function in the call stack.\n     */\n    function _reentrancyGuardEntered() internal view returns (bool) {\n        ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();\n        return $._status == ENTERED;\n    }\n}\n"},{"file_path":"@openzeppelin/contracts-upgradeable/utils/cryptography/EIP712Upgradeable.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/EIP712.sol)\n\npragma solidity ^0.8.20;\n\nimport {MessageHashUtils} from \"@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol\";\nimport {IERC5267} from \"@openzeppelin/contracts/interfaces/IERC5267.sol\";\nimport {Initializable} from \"../../proxy/utils/Initializable.sol\";\n\n/**\n * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.\n *\n * The encoding scheme specified in the EIP requires a domain separator and a hash of the typed structured data, whose\n * encoding is very generic and therefore its implementation in Solidity is not feasible, thus this contract\n * does not implement the encoding itself. Protocols need to implement the type-specific encoding they need in order to\n * produce the hash of their typed data using a combination of `abi.encode` and `keccak256`.\n *\n * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding\n * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA\n * ({_hashTypedDataV4}).\n *\n * The implementation of the domain separator was designed to be as efficient as possible while still properly updating\n * the chain id to protect against replay attacks on an eventual fork of the chain.\n *\n * NOTE: This contract implements the version of the encoding known as \"v4\", as implemented by the JSON RPC method\n * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].\n *\n * NOTE: In the upgradeable version of this contract, the cached values will correspond to the address, and the domain\n * separator of the implementation contract. This will cause the {_domainSeparatorV4} function to always rebuild the\n * separator from the immutable values, which is cheaper than accessing a cached version in cold storage.\n */\nabstract contract EIP712Upgradeable is Initializable, IERC5267 {\n    bytes32 private constant TYPE_HASH =\n        keccak256(\"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)\");\n\n    /// @custom:storage-location erc7201:openzeppelin.storage.EIP712\n    struct EIP712Storage {\n        /// @custom:oz-renamed-from _HASHED_NAME\n        bytes32 _hashedName;\n        /// @custom:oz-renamed-from _HASHED_VERSION\n        bytes32 _hashedVersion;\n\n        string _name;\n        string _version;\n    }\n\n    // keccak256(abi.encode(uint256(keccak256(\"openzeppelin.storage.EIP712\")) - 1)) & ~bytes32(uint256(0xff))\n    bytes32 private constant EIP712StorageLocation = 0xa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d100;\n\n    function _getEIP712Storage() private pure returns (EIP712Storage storage $) {\n        assembly {\n            $.slot := EIP712StorageLocation\n        }\n    }\n\n    /**\n     * @dev Initializes the domain separator and parameter caches.\n     *\n     * The meaning of `name` and `version` is specified in\n     * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:\n     *\n     * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.\n     * - `version`: the current major version of the signing domain.\n     *\n     * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart\n     * contract upgrade].\n     */\n    function __EIP712_init(string memory name, string memory version) internal onlyInitializing {\n        __EIP712_init_unchained(name, version);\n    }\n\n    function __EIP712_init_unchained(string memory name, string memory version) internal onlyInitializing {\n        EIP712Storage storage $ = _getEIP712Storage();\n        $._name = name;\n        $._version = version;\n\n        // Reset prior values in storage if upgrading\n        $._hashedName = 0;\n        $._hashedVersion = 0;\n    }\n\n    /**\n     * @dev Returns the domain separator for the current chain.\n     */\n    function _domainSeparatorV4() internal view returns (bytes32) {\n        return _buildDomainSeparator();\n    }\n\n    function _buildDomainSeparator() private view returns (bytes32) {\n        return keccak256(abi.encode(TYPE_HASH, _EIP712NameHash(), _EIP712VersionHash(), block.chainid, address(this)));\n    }\n\n    /**\n     * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this\n     * function returns the hash of the fully encoded EIP712 message for this domain.\n     *\n     * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:\n     *\n     * ```solidity\n     * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(\n     *     keccak256(\"Mail(address to,string contents)\"),\n     *     mailTo,\n     *     keccak256(bytes(mailContents))\n     * )));\n     * address signer = ECDSA.recover(digest, signature);\n     * ```\n     */\n    function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {\n        return MessageHashUtils.toTypedDataHash(_domainSeparatorV4(), structHash);\n    }\n\n    /**\n     * @dev See {IERC-5267}.\n     */\n    function eip712Domain()\n        public\n        view\n        virtual\n        returns (\n            bytes1 fields,\n            string memory name,\n            string memory version,\n            uint256 chainId,\n            address verifyingContract,\n            bytes32 salt,\n            uint256[] memory extensions\n        )\n    {\n        EIP712Storage storage $ = _getEIP712Storage();\n        // If the hashed name and version in storage are non-zero, the contract hasn't been properly initialized\n        // and the EIP712 domain is not reliable, as it will be missing name and version.\n        require($._hashedName == 0 && $._hashedVersion == 0, \"EIP712: Uninitialized\");\n\n        return (\n            hex\"0f\", // 01111\n            _EIP712Name(),\n            _EIP712Version(),\n            block.chainid,\n            address(this),\n            bytes32(0),\n            new uint256[](0)\n        );\n    }\n\n    /**\n     * @dev The name parameter for the EIP712 domain.\n     *\n     * NOTE: This function reads from storage by default, but can be redefined to return a constant value if gas costs\n     * are a concern.\n     */\n    function _EIP712Name() internal view virtual returns (string memory) {\n        EIP712Storage storage $ = _getEIP712Storage();\n        return $._name;\n    }\n\n    /**\n     * @dev The version parameter for the EIP712 domain.\n     *\n     * NOTE: This function reads from storage by default, but can be redefined to return a constant value if gas costs\n     * are a concern.\n     */\n    function _EIP712Version() internal view virtual returns (string memory) {\n        EIP712Storage storage $ = _getEIP712Storage();\n        return $._version;\n    }\n\n    /**\n     * @dev The hash of the name parameter for the EIP712 domain.\n     *\n     * NOTE: In previous versions this function was virtual. In this version you should override `_EIP712Name` instead.\n     */\n    function _EIP712NameHash() internal view returns (bytes32) {\n        EIP712Storage storage $ = _getEIP712Storage();\n        string memory name = _EIP712Name();\n        if (bytes(name).length > 0) {\n            return keccak256(bytes(name));\n        } else {\n            // If the name is empty, the contract may have been upgraded without initializing the new storage.\n            // We return the name hash in storage if non-zero, otherwise we assume the name is empty by design.\n            bytes32 hashedName = $._hashedName;\n            if (hashedName != 0) {\n                return hashedName;\n            } else {\n                return keccak256(\"\");\n            }\n        }\n    }\n\n    /**\n     * @dev The hash of the version parameter for the EIP712 domain.\n     *\n     * NOTE: In previous versions this function was virtual. In this version you should override `_EIP712Version` instead.\n     */\n    function _EIP712VersionHash() internal view returns (bytes32) {\n        EIP712Storage storage $ = _getEIP712Storage();\n        string memory version = _EIP712Version();\n        if (bytes(version).length > 0) {\n            return keccak256(bytes(version));\n        } else {\n            // If the version is empty, the contract may have been upgraded without initializing the new storage.\n            // We return the version hash in storage if non-zero, otherwise we assume the version is empty by design.\n            bytes32 hashedVersion = $._hashedVersion;\n            if (hashedVersion != 0) {\n                return hashedVersion;\n            } else {\n                return keccak256(\"\");\n            }\n        }\n    }\n}\n"},{"file_path":"@openzeppelin/contracts/interfaces/IERC1271.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC1271.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Interface of the ERC1271 standard signature validation method for\n * contracts as defined in https://eips.ethereum.org/EIPS/eip-1271[ERC-1271].\n */\ninterface IERC1271 {\n    /**\n     * @dev Should return whether the signature provided is valid for the provided data\n     * @param hash      Hash of the data to be signed\n     * @param signature Signature byte array associated with _data\n     */\n    function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue);\n}\n"},{"file_path":"@openzeppelin/contracts/interfaces/IERC5267.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC5267.sol)\n\npragma solidity ^0.8.20;\n\ninterface IERC5267 {\n    /**\n     * @dev MAY be emitted to signal that the domain could have changed.\n     */\n    event EIP712DomainChanged();\n\n    /**\n     * @dev returns the fields and values that describe the domain separator used by this contract for EIP-712\n     * signature.\n     */\n    function eip712Domain()\n        external\n        view\n        returns (\n            bytes1 fields,\n            string memory name,\n            string memory version,\n            uint256 chainId,\n            address verifyingContract,\n            bytes32 salt,\n            uint256[] memory extensions\n        );\n}\n"},{"file_path":"@openzeppelin/contracts/token/ERC20/IERC20.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n    /**\n     * @dev Emitted when `value` tokens are moved from one account (`from`) to\n     * another (`to`).\n     *\n     * Note that `value` may be zero.\n     */\n    event Transfer(address indexed from, address indexed to, uint256 value);\n\n    /**\n     * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n     * a call to {approve}. `value` is the new allowance.\n     */\n    event Approval(address indexed owner, address indexed spender, uint256 value);\n\n    /**\n     * @dev Returns the value of tokens in existence.\n     */\n    function totalSupply() external view returns (uint256);\n\n    /**\n     * @dev Returns the value of tokens owned by `account`.\n     */\n    function balanceOf(address account) external view returns (uint256);\n\n    /**\n     * @dev Moves a `value` amount of tokens from the caller's account to `to`.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transfer(address to, uint256 value) external returns (bool);\n\n    /**\n     * @dev Returns the remaining number of tokens that `spender` will be\n     * allowed to spend on behalf of `owner` through {transferFrom}. This is\n     * zero by default.\n     *\n     * This value changes when {approve} or {transferFrom} are called.\n     */\n    function allowance(address owner, address spender) external view returns (uint256);\n\n    /**\n     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the\n     * caller's tokens.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * IMPORTANT: Beware that changing an allowance with this method brings the risk\n     * that someone may use both the old and the new allowance by unfortunate\n     * transaction ordering. One possible solution to mitigate this race\n     * condition is to first reduce the spender's allowance to 0 and set the\n     * desired value afterwards:\n     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n     *\n     * Emits an {Approval} event.\n     */\n    function approve(address spender, uint256 value) external returns (bool);\n\n    /**\n     * @dev Moves a `value` amount of tokens from `from` to `to` using the\n     * allowance mechanism. `value` is then deducted from the caller's\n     * allowance.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transferFrom(address from, address to, uint256 value) external returns (bool);\n}\n"},{"file_path":"@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in\n * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].\n *\n * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by\n * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't\n * need to send a transaction, and thus is not required to hold Ether at all.\n *\n * ==== Security Considerations\n *\n * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature\n * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be\n * considered as an intention to spend the allowance in any specific way. The second is that because permits have\n * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should\n * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be\n * generally recommended is:\n *\n * ```solidity\n * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {\n *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}\n *     doThing(..., value);\n * }\n *\n * function doThing(..., uint256 value) public {\n *     token.safeTransferFrom(msg.sender, address(this), value);\n *     ...\n * }\n * ```\n *\n * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of\n * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also\n * {SafeERC20-safeTransferFrom}).\n *\n * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so\n * contracts should have entry points that don't rely on permit.\n */\ninterface IERC20Permit {\n    /**\n     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,\n     * given ``owner``'s signed approval.\n     *\n     * IMPORTANT: The same issues {IERC20-approve} has related to transaction\n     * ordering also apply here.\n     *\n     * Emits an {Approval} event.\n     *\n     * Requirements:\n     *\n     * - `spender` cannot be the zero address.\n     * - `deadline` must be a timestamp in the future.\n     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`\n     * over the EIP712-formatted function arguments.\n     * - the signature must use ``owner``'s current nonce (see {nonces}).\n     *\n     * For more information on the signature format, see the\n     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP\n     * section].\n     *\n     * CAUTION: See Security Considerations above.\n     */\n    function permit(\n        address owner,\n        address spender,\n        uint256 value,\n        uint256 deadline,\n        uint8 v,\n        bytes32 r,\n        bytes32 s\n    ) external;\n\n    /**\n     * @dev Returns the current nonce for `owner`. This value must be\n     * included whenever a signature is generated for {permit}.\n     *\n     * Every successful call to {permit} increases ``owner``'s nonce by one. This\n     * prevents a signature from being used multiple times.\n     */\n    function nonces(address owner) external view returns (uint256);\n\n    /**\n     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.\n     */\n    // solhint-disable-next-line func-name-mixedcase\n    function DOMAIN_SEPARATOR() external view returns (bytes32);\n}\n"},{"file_path":"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)\n\npragma solidity ^0.8.20;\n\nimport {IERC20} from \"../IERC20.sol\";\nimport {IERC20Permit} from \"../extensions/IERC20Permit.sol\";\nimport {Address} from \"../../../utils/Address.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n    using Address for address;\n\n    /**\n     * @dev An operation with an ERC20 token failed.\n     */\n    error SafeERC20FailedOperation(address token);\n\n    /**\n     * @dev Indicates a failed `decreaseAllowance` request.\n     */\n    error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);\n\n    /**\n     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,\n     * non-reverting calls are assumed to be successful.\n     */\n    function safeTransfer(IERC20 token, address to, uint256 value) internal {\n        _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));\n    }\n\n    /**\n     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the\n     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.\n     */\n    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {\n        _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));\n    }\n\n    /**\n     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,\n     * non-reverting calls are assumed to be successful.\n     */\n    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {\n        uint256 oldAllowance = token.allowance(address(this), spender);\n        forceApprove(token, spender, oldAllowance + value);\n    }\n\n    /**\n     * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no\n     * value, non-reverting calls are assumed to be successful.\n     */\n    function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {\n        unchecked {\n            uint256 currentAllowance = token.allowance(address(this), spender);\n            if (currentAllowance < requestedDecrease) {\n                revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);\n            }\n            forceApprove(token, spender, currentAllowance - requestedDecrease);\n        }\n    }\n\n    /**\n     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,\n     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval\n     * to be set to zero before setting it to a non-zero value, such as USDT.\n     */\n    function forceApprove(IERC20 token, address spender, uint256 value) internal {\n        bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));\n\n        if (!_callOptionalReturnBool(token, approvalCall)) {\n            _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));\n            _callOptionalReturn(token, approvalCall);\n        }\n    }\n\n    /**\n     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n     * on the return value: the return value is optional (but if data is returned, it must not be false).\n     * @param token The token targeted by the call.\n     * @param data The call data (encoded using abi.encode or one of its variants).\n     */\n    function _callOptionalReturn(IERC20 token, bytes memory data) private {\n        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n        // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that\n        // the target address contains contract code and also asserts for success in the low-level call.\n\n        bytes memory returndata = address(token).functionCall(data);\n        if (returndata.length != 0 && !abi.decode(returndata, (bool))) {\n            revert SafeERC20FailedOperation(address(token));\n        }\n    }\n\n    /**\n     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n     * on the return value: the return value is optional (but if data is returned, it must not be false).\n     * @param token The token targeted by the call.\n     * @param data The call data (encoded using abi.encode or one of its variants).\n     *\n     * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.\n     */\n    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {\n        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n        // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false\n        // and not revert is the subcall reverts.\n\n        (bool success, bytes memory returndata) = address(token).call(data);\n        return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;\n    }\n}\n"},{"file_path":"@openzeppelin/contracts/utils/Address.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n    /**\n     * @dev The ETH balance of the account is not enough to perform the operation.\n     */\n    error AddressInsufficientBalance(address account);\n\n    /**\n     * @dev There's no code at `target` (it is not a contract).\n     */\n    error AddressEmptyCode(address target);\n\n    /**\n     * @dev A call to an address target failed. The target may have reverted.\n     */\n    error FailedInnerCall();\n\n    /**\n     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n     * `recipient`, forwarding all available gas and reverting on errors.\n     *\n     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n     * of certain opcodes, possibly making contracts go over the 2300 gas limit\n     * imposed by `transfer`, making them unable to receive funds via\n     * `transfer`. {sendValue} removes this limitation.\n     *\n     * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n     *\n     * IMPORTANT: because control is transferred to `recipient`, care must be\n     * taken to not create reentrancy vulnerabilities. Consider using\n     * {ReentrancyGuard} or the\n     * https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n     */\n    function sendValue(address payable recipient, uint256 amount) internal {\n        if (address(this).balance < amount) {\n            revert AddressInsufficientBalance(address(this));\n        }\n\n        (bool success, ) = recipient.call{value: amount}(\"\");\n        if (!success) {\n            revert FailedInnerCall();\n        }\n    }\n\n    /**\n     * @dev Performs a Solidity function call using a low level `call`. A\n     * plain `call` is an unsafe replacement for a function call: use this\n     * function instead.\n     *\n     * If `target` reverts with a revert reason or custom error, it is bubbled\n     * up by this function (like regular Solidity function calls). However, if\n     * the call reverted with no returned reason, this function reverts with a\n     * {FailedInnerCall} error.\n     *\n     * Returns the raw returned data. To convert to the expected return value,\n     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n     *\n     * Requirements:\n     *\n     * - `target` must be a contract.\n     * - calling `target` with `data` must not revert.\n     */\n    function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n        return functionCallWithValue(target, data, 0);\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n     * but also transferring `value` wei to `target`.\n     *\n     * Requirements:\n     *\n     * - the calling contract must have an ETH balance of at least `value`.\n     * - the called Solidity function must be `payable`.\n     */\n    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {\n        if (address(this).balance < value) {\n            revert AddressInsufficientBalance(address(this));\n        }\n        (bool success, bytes memory returndata) = target.call{value: value}(data);\n        return verifyCallResultFromTarget(target, success, returndata);\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n     * but performing a static call.\n     */\n    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n        (bool success, bytes memory returndata) = target.staticcall(data);\n        return verifyCallResultFromTarget(target, success, returndata);\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n     * but performing a delegate call.\n     */\n    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n        (bool success, bytes memory returndata) = target.delegatecall(data);\n        return verifyCallResultFromTarget(target, success, returndata);\n    }\n\n    /**\n     * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target\n     * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an\n     * unsuccessful call.\n     */\n    function verifyCallResultFromTarget(\n        address target,\n        bool success,\n        bytes memory returndata\n    ) internal view returns (bytes memory) {\n        if (!success) {\n            _revert(returndata);\n        } else {\n            // only check if target is a contract if the call was successful and the return data is empty\n            // otherwise we already know that it was a contract\n            if (returndata.length == 0 && target.code.length == 0) {\n                revert AddressEmptyCode(target);\n            }\n            return returndata;\n        }\n    }\n\n    /**\n     * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the\n     * revert reason or with a default {FailedInnerCall} error.\n     */\n    function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {\n        if (!success) {\n            _revert(returndata);\n        } else {\n            return returndata;\n        }\n    }\n\n    /**\n     * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.\n     */\n    function _revert(bytes memory returndata) private pure {\n        // Look for revert reason and bubble it up if present\n        if (returndata.length > 0) {\n            // The easiest way to bubble the revert reason is using memory via assembly\n            /// @solidity memory-safe-assembly\n            assembly {\n                let returndata_size := mload(returndata)\n                revert(add(32, returndata), returndata_size)\n            }\n        } else {\n            revert FailedInnerCall();\n        }\n    }\n}\n"},{"file_path":"@openzeppelin/contracts/utils/Strings.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (utils/Strings.sol)\n\npragma solidity ^0.8.20;\n\nimport {Math} from \"./math/Math.sol\";\nimport {SignedMath} from \"./math/SignedMath.sol\";\n\n/**\n * @dev String operations.\n */\nlibrary Strings {\n    bytes16 private constant HEX_DIGITS = \"0123456789abcdef\";\n    uint8 private constant ADDRESS_LENGTH = 20;\n\n    /**\n     * @dev The `value` string doesn't fit in the specified `length`.\n     */\n    error StringsInsufficientHexLength(uint256 value, uint256 length);\n\n    /**\n     * @dev Converts a `uint256` to its ASCII `string` decimal representation.\n     */\n    function toString(uint256 value) internal pure returns (string memory) {\n        unchecked {\n            uint256 length = Math.log10(value) + 1;\n            string memory buffer = new string(length);\n            uint256 ptr;\n            /// @solidity memory-safe-assembly\n            assembly {\n                ptr := add(buffer, add(32, length))\n            }\n            while (true) {\n                ptr--;\n                /// @solidity memory-safe-assembly\n                assembly {\n                    mstore8(ptr, byte(mod(value, 10), HEX_DIGITS))\n                }\n                value /= 10;\n                if (value == 0) break;\n            }\n            return buffer;\n        }\n    }\n\n    /**\n     * @dev Converts a `int256` to its ASCII `string` decimal representation.\n     */\n    function toStringSigned(int256 value) internal pure returns (string memory) {\n        return string.concat(value < 0 ? \"-\" : \"\", toString(SignedMath.abs(value)));\n    }\n\n    /**\n     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.\n     */\n    function toHexString(uint256 value) internal pure returns (string memory) {\n        unchecked {\n            return toHexString(value, Math.log256(value) + 1);\n        }\n    }\n\n    /**\n     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.\n     */\n    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {\n        uint256 localValue = value;\n        bytes memory buffer = new bytes(2 * length + 2);\n        buffer[0] = \"0\";\n        buffer[1] = \"x\";\n        for (uint256 i = 2 * length + 1; i > 1; --i) {\n            buffer[i] = HEX_DIGITS[localValue & 0xf];\n            localValue >>= 4;\n        }\n        if (localValue != 0) {\n            revert StringsInsufficientHexLength(value, length);\n        }\n        return string(buffer);\n    }\n\n    /**\n     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal\n     * representation.\n     */\n    function toHexString(address addr) internal pure returns (string memory) {\n        return toHexString(uint256(uint160(addr)), ADDRESS_LENGTH);\n    }\n\n    /**\n     * @dev Returns true if the two strings are equal.\n     */\n    function equal(string memory a, string memory b) internal pure returns (bool) {\n        return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b));\n    }\n}\n"},{"file_path":"@openzeppelin/contracts/utils/cryptography/ECDSA.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/ECDSA.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.\n *\n * These functions can be used to verify that a message was signed by the holder\n * of the private keys of a given address.\n */\nlibrary ECDSA {\n    enum RecoverError {\n        NoError,\n        InvalidSignature,\n        InvalidSignatureLength,\n        InvalidSignatureS\n    }\n\n    /**\n     * @dev The signature derives the `address(0)`.\n     */\n    error ECDSAInvalidSignature();\n\n    /**\n     * @dev The signature has an invalid length.\n     */\n    error ECDSAInvalidSignatureLength(uint256 length);\n\n    /**\n     * @dev The signature has an S value that is in the upper half order.\n     */\n    error ECDSAInvalidSignatureS(bytes32 s);\n\n    /**\n     * @dev Returns the address that signed a hashed message (`hash`) with `signature` or an error. This will not\n     * return address(0) without also returning an error description. Errors are documented using an enum (error type)\n     * and a bytes32 providing additional information about the error.\n     *\n     * If no error is returned, then the address can be used for verification purposes.\n     *\n     * The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:\n     * this function rejects them by requiring the `s` value to be in the lower\n     * half order, and the `v` value to be either 27 or 28.\n     *\n     * IMPORTANT: `hash` _must_ be the result of a hash operation for the\n     * verification to be secure: it is possible to craft signatures that\n     * recover to arbitrary addresses for non-hashed data. A safe way to ensure\n     * this is by receiving a hash of the original message (which may otherwise\n     * be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.\n     *\n     * Documentation for signature generation:\n     * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]\n     * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]\n     */\n    function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError, bytes32) {\n        if (signature.length == 65) {\n            bytes32 r;\n            bytes32 s;\n            uint8 v;\n            // ecrecover takes the signature parameters, and the only way to get them\n            // currently is to use assembly.\n            /// @solidity memory-safe-assembly\n            assembly {\n                r := mload(add(signature, 0x20))\n                s := mload(add(signature, 0x40))\n                v := byte(0, mload(add(signature, 0x60)))\n            }\n            return tryRecover(hash, v, r, s);\n        } else {\n            return (address(0), RecoverError.InvalidSignatureLength, bytes32(signature.length));\n        }\n    }\n\n    /**\n     * @dev Returns the address that signed a hashed message (`hash`) with\n     * `signature`. This address can then be used for verification purposes.\n     *\n     * The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:\n     * this function rejects them by requiring the `s` value to be in the lower\n     * half order, and the `v` value to be either 27 or 28.\n     *\n     * IMPORTANT: `hash` _must_ be the result of a hash operation for the\n     * verification to be secure: it is possible to craft signatures that\n     * recover to arbitrary addresses for non-hashed data. A safe way to ensure\n     * this is by receiving a hash of the original message (which may otherwise\n     * be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.\n     */\n    function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {\n        (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, signature);\n        _throwError(error, errorArg);\n        return recovered;\n    }\n\n    /**\n     * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.\n     *\n     * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]\n     */\n    function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError, bytes32) {\n        unchecked {\n            bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);\n            // We do not check for an overflow here since the shift operation results in 0 or 1.\n            uint8 v = uint8((uint256(vs) >> 255) + 27);\n            return tryRecover(hash, v, r, s);\n        }\n    }\n\n    /**\n     * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.\n     */\n    function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {\n        (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, r, vs);\n        _throwError(error, errorArg);\n        return recovered;\n    }\n\n    /**\n     * @dev Overload of {ECDSA-tryRecover} that receives the `v`,\n     * `r` and `s` signature fields separately.\n     */\n    function tryRecover(\n        bytes32 hash,\n        uint8 v,\n        bytes32 r,\n        bytes32 s\n    ) internal pure returns (address, RecoverError, bytes32) {\n        // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature\n        // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines\n        // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most\n        // signatures from current libraries generate a unique signature with an s-value in the lower half order.\n        //\n        // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value\n        // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or\n        // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept\n        // these malleable signatures as well.\n        if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {\n            return (address(0), RecoverError.InvalidSignatureS, s);\n        }\n\n        // If the signature is valid (and not malleable), return the signer address\n        address signer = ecrecover(hash, v, r, s);\n        if (signer == address(0)) {\n            return (address(0), RecoverError.InvalidSignature, bytes32(0));\n        }\n\n        return (signer, RecoverError.NoError, bytes32(0));\n    }\n\n    /**\n     * @dev Overload of {ECDSA-recover} that receives the `v`,\n     * `r` and `s` signature fields separately.\n     */\n    function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {\n        (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, v, r, s);\n        _throwError(error, errorArg);\n        return recovered;\n    }\n\n    /**\n     * @dev Optionally reverts with the corresponding custom error according to the `error` argument provided.\n     */\n    function _throwError(RecoverError error, bytes32 errorArg) private pure {\n        if (error == RecoverError.NoError) {\n            return; // no error: do nothing\n        } else if (error == RecoverError.InvalidSignature) {\n            revert ECDSAInvalidSignature();\n        } else if (error == RecoverError.InvalidSignatureLength) {\n            revert ECDSAInvalidSignatureLength(uint256(errorArg));\n        } else if (error == RecoverError.InvalidSignatureS) {\n            revert ECDSAInvalidSignatureS(errorArg);\n        }\n    }\n}\n"},{"file_path":"@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/MessageHashUtils.sol)\n\npragma solidity ^0.8.20;\n\nimport {Strings} from \"../Strings.sol\";\n\n/**\n * @dev Signature message hash utilities for producing digests to be consumed by {ECDSA} recovery or signing.\n *\n * The library provides methods for generating a hash of a message that conforms to the\n * https://eips.ethereum.org/EIPS/eip-191[EIP 191] and https://eips.ethereum.org/EIPS/eip-712[EIP 712]\n * specifications.\n */\nlibrary MessageHashUtils {\n    /**\n     * @dev Returns the keccak256 digest of an EIP-191 signed data with version\n     * `0x45` (`personal_sign` messages).\n     *\n     * The digest is calculated by prefixing a bytes32 `messageHash` with\n     * `\"\\x19Ethereum Signed Message:\\n32\"` and hashing the result. It corresponds with the\n     * hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method.\n     *\n     * NOTE: The `messageHash` parameter is intended to be the result of hashing a raw message with\n     * keccak256, although any bytes32 value can be safely used because the final digest will\n     * be re-hashed.\n     *\n     * See {ECDSA-recover}.\n     */\n    function toEthSignedMessageHash(bytes32 messageHash) internal pure returns (bytes32 digest) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            mstore(0x00, \"\\x19Ethereum Signed Message:\\n32\") // 32 is the bytes-length of messageHash\n            mstore(0x1c, messageHash) // 0x1c (28) is the length of the prefix\n            digest := keccak256(0x00, 0x3c) // 0x3c is the length of the prefix (0x1c) + messageHash (0x20)\n        }\n    }\n\n    /**\n     * @dev Returns the keccak256 digest of an EIP-191 signed data with version\n     * `0x45` (`personal_sign` messages).\n     *\n     * The digest is calculated by prefixing an arbitrary `message` with\n     * `\"\\x19Ethereum Signed Message:\\n\" + len(message)` and hashing the result. It corresponds with the\n     * hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method.\n     *\n     * See {ECDSA-recover}.\n     */\n    function toEthSignedMessageHash(bytes memory message) internal pure returns (bytes32) {\n        return\n            keccak256(bytes.concat(\"\\x19Ethereum Signed Message:\\n\", bytes(Strings.toString(message.length)), message));\n    }\n\n    /**\n     * @dev Returns the keccak256 digest of an EIP-191 signed data with version\n     * `0x00` (data with intended validator).\n     *\n     * The digest is calculated by prefixing an arbitrary `data` with `\"\\x19\\x00\"` and the intended\n     * `validator` address. Then hashing the result.\n     *\n     * See {ECDSA-recover}.\n     */\n    function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {\n        return keccak256(abi.encodePacked(hex\"19_00\", validator, data));\n    }\n\n    /**\n     * @dev Returns the keccak256 digest of an EIP-712 typed data (EIP-191 version `0x01`).\n     *\n     * The digest is calculated from a `domainSeparator` and a `structHash`, by prefixing them with\n     * `\\x19\\x01` and hashing the result. It corresponds to the hash signed by the\n     * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] JSON-RPC method as part of EIP-712.\n     *\n     * See {ECDSA-recover}.\n     */\n    function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 digest) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            let ptr := mload(0x40)\n            mstore(ptr, hex\"19_01\")\n            mstore(add(ptr, 0x02), domainSeparator)\n            mstore(add(ptr, 0x22), structHash)\n            digest := keccak256(ptr, 0x42)\n        }\n    }\n}\n"},{"file_path":"@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/SignatureChecker.sol)\n\npragma solidity ^0.8.20;\n\nimport {ECDSA} from \"./ECDSA.sol\";\nimport {IERC1271} from \"../../interfaces/IERC1271.sol\";\n\n/**\n * @dev Signature verification helper that can be used instead of `ECDSA.recover` to seamlessly support both ECDSA\n * signatures from externally owned accounts (EOAs) as well as ERC1271 signatures from smart contract wallets like\n * Argent and Safe Wallet (previously Gnosis Safe).\n */\nlibrary SignatureChecker {\n    /**\n     * @dev Checks if a signature is valid for a given signer and data hash. If the signer is a smart contract, the\n     * signature is validated against that smart contract using ERC1271, otherwise it's validated using `ECDSA.recover`.\n     *\n     * NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus\n     * change through time. It could return true at block N and false at block N+1 (or the opposite).\n     */\n    function isValidSignatureNow(address signer, bytes32 hash, bytes memory signature) internal view returns (bool) {\n        (address recovered, ECDSA.RecoverError error, ) = ECDSA.tryRecover(hash, signature);\n        return\n            (error == ECDSA.RecoverError.NoError && recovered == signer) ||\n            isValidERC1271SignatureNow(signer, hash, signature);\n    }\n\n    /**\n     * @dev Checks if a signature is valid for a given signer and data hash. The signature is validated\n     * against the signer smart contract using ERC1271.\n     *\n     * NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus\n     * change through time. It could return true at block N and false at block N+1 (or the opposite).\n     */\n    function isValidERC1271SignatureNow(\n        address signer,\n        bytes32 hash,\n        bytes memory signature\n    ) internal view returns (bool) {\n        (bool success, bytes memory result) = signer.staticcall(\n            abi.encodeCall(IERC1271.isValidSignature, (hash, signature))\n        );\n        return (success &&\n            result.length >= 32 &&\n            abi.decode(result, (bytes32)) == bytes32(IERC1271.isValidSignature.selector));\n    }\n}\n"},{"file_path":"@openzeppelin/contracts/utils/introspection/IERC165.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Interface of the ERC165 standard, as defined in the\n * https://eips.ethereum.org/EIPS/eip-165[EIP].\n *\n * Implementers can declare support of contract interfaces, which can then be\n * queried by others ({ERC165Checker}).\n *\n * For an implementation, see {ERC165}.\n */\ninterface IERC165 {\n    /**\n     * @dev Returns true if this contract implements the interface defined by\n     * `interfaceId`. See the corresponding\n     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]\n     * to learn more about how these ids are created.\n     *\n     * This function call must use less than 30 000 gas.\n     */\n    function supportsInterface(bytes4 interfaceId) external view returns (bool);\n}\n"},{"file_path":"@openzeppelin/contracts/utils/math/Math.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Standard math utilities missing in the Solidity language.\n */\nlibrary Math {\n    /**\n     * @dev Muldiv operation overflow.\n     */\n    error MathOverflowedMulDiv();\n\n    enum Rounding {\n        Floor, // Toward negative infinity\n        Ceil, // Toward positive infinity\n        Trunc, // Toward zero\n        Expand // Away from zero\n    }\n\n    /**\n     * @dev Returns the addition of two unsigned integers, with an overflow flag.\n     */\n    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n        unchecked {\n            uint256 c = a + b;\n            if (c < a) return (false, 0);\n            return (true, c);\n        }\n    }\n\n    /**\n     * @dev Returns the subtraction of two unsigned integers, with an overflow flag.\n     */\n    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n        unchecked {\n            if (b > a) return (false, 0);\n            return (true, a - b);\n        }\n    }\n\n    /**\n     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.\n     */\n    function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n        unchecked {\n            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the\n            // benefit is lost if 'b' is also tested.\n            // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522\n            if (a == 0) return (true, 0);\n            uint256 c = a * b;\n            if (c / a != b) return (false, 0);\n            return (true, c);\n        }\n    }\n\n    /**\n     * @dev Returns the division of two unsigned integers, with a division by zero flag.\n     */\n    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n        unchecked {\n            if (b == 0) return (false, 0);\n            return (true, a / b);\n        }\n    }\n\n    /**\n     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.\n     */\n    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n        unchecked {\n            if (b == 0) return (false, 0);\n            return (true, a % b);\n        }\n    }\n\n    /**\n     * @dev Returns the largest of two numbers.\n     */\n    function max(uint256 a, uint256 b) internal pure returns (uint256) {\n        return a > b ? a : b;\n    }\n\n    /**\n     * @dev Returns the smallest of two numbers.\n     */\n    function min(uint256 a, uint256 b) internal pure returns (uint256) {\n        return a < b ? a : b;\n    }\n\n    /**\n     * @dev Returns the average of two numbers. The result is rounded towards\n     * zero.\n     */\n    function average(uint256 a, uint256 b) internal pure returns (uint256) {\n        // (a + b) / 2 can overflow.\n        return (a & b) + (a ^ b) / 2;\n    }\n\n    /**\n     * @dev Returns the ceiling of the division of two numbers.\n     *\n     * This differs from standard division with `/` in that it rounds towards infinity instead\n     * of rounding towards zero.\n     */\n    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {\n        if (b == 0) {\n            // Guarantee the same behavior as in a regular Solidity division.\n            return a / b;\n        }\n\n        // (a + b - 1) / b can overflow on addition, so we distribute.\n        return a == 0 ? 0 : (a - 1) / b + 1;\n    }\n\n    /**\n     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or\n     * denominator == 0.\n     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by\n     * Uniswap Labs also under MIT license.\n     */\n    function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {\n        unchecked {\n            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use\n            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256\n            // variables such that product = prod1 * 2^256 + prod0.\n            uint256 prod0 = x * y; // Least significant 256 bits of the product\n            uint256 prod1; // Most significant 256 bits of the product\n            assembly {\n                let mm := mulmod(x, y, not(0))\n                prod1 := sub(sub(mm, prod0), lt(mm, prod0))\n            }\n\n            // Handle non-overflow cases, 256 by 256 division.\n            if (prod1 == 0) {\n                // Solidity will revert if denominator == 0, unlike the div opcode on its own.\n                // The surrounding unchecked block does not change this fact.\n                // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.\n                return prod0 / denominator;\n            }\n\n            // Make sure the result is less than 2^256. Also prevents denominator == 0.\n            if (denominator <= prod1) {\n                revert MathOverflowedMulDiv();\n            }\n\n            ///////////////////////////////////////////////\n            // 512 by 256 division.\n            ///////////////////////////////////////////////\n\n            // Make division exact by subtracting the remainder from [prod1 prod0].\n            uint256 remainder;\n            assembly {\n                // Compute remainder using mulmod.\n                remainder := mulmod(x, y, denominator)\n\n                // Subtract 256 bit number from 512 bit number.\n                prod1 := sub(prod1, gt(remainder, prod0))\n                prod0 := sub(prod0, remainder)\n            }\n\n            // Factor powers of two out of denominator and compute largest power of two divisor of denominator.\n            // Always >= 1. See https://cs.stackexchange.com/q/138556/92363.\n\n            uint256 twos = denominator & (0 - denominator);\n            assembly {\n                // Divide denominator by twos.\n                denominator := div(denominator, twos)\n\n                // Divide [prod1 prod0] by twos.\n                prod0 := div(prod0, twos)\n\n                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.\n                twos := add(div(sub(0, twos), twos), 1)\n            }\n\n            // Shift in bits from prod1 into prod0.\n            prod0 |= prod1 * twos;\n\n            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such\n            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for\n            // four bits. That is, denominator * inv = 1 mod 2^4.\n            uint256 inverse = (3 * denominator) ^ 2;\n\n            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also\n            // works in modular arithmetic, doubling the correct bits in each step.\n            inverse *= 2 - denominator * inverse; // inverse mod 2^8\n            inverse *= 2 - denominator * inverse; // inverse mod 2^16\n            inverse *= 2 - denominator * inverse; // inverse mod 2^32\n            inverse *= 2 - denominator * inverse; // inverse mod 2^64\n            inverse *= 2 - denominator * inverse; // inverse mod 2^128\n            inverse *= 2 - denominator * inverse; // inverse mod 2^256\n\n            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.\n            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is\n            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1\n            // is no longer required.\n            result = prod0 * inverse;\n            return result;\n        }\n    }\n\n    /**\n     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.\n     */\n    function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {\n        uint256 result = mulDiv(x, y, denominator);\n        if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {\n            result += 1;\n        }\n        return result;\n    }\n\n    /**\n     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded\n     * towards zero.\n     *\n     * Inspired by Henry S. Warren, Jr.'s \"Hacker's Delight\" (Chapter 11).\n     */\n    function sqrt(uint256 a) internal pure returns (uint256) {\n        if (a == 0) {\n            return 0;\n        }\n\n        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.\n        //\n        // We know that the \"msb\" (most significant bit) of our target number `a` is a power of 2 such that we have\n        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.\n        //\n        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`\n        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`\n        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`\n        //\n        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.\n        uint256 result = 1 << (log2(a) >> 1);\n\n        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,\n        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at\n        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision\n        // into the expected uint128 result.\n        unchecked {\n            result = (result + a / result) >> 1;\n            result = (result + a / result) >> 1;\n            result = (result + a / result) >> 1;\n            result = (result + a / result) >> 1;\n            result = (result + a / result) >> 1;\n            result = (result + a / result) >> 1;\n            result = (result + a / result) >> 1;\n            return min(result, a / result);\n        }\n    }\n\n    /**\n     * @notice Calculates sqrt(a), following the selected rounding direction.\n     */\n    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {\n        unchecked {\n            uint256 result = sqrt(a);\n            return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);\n        }\n    }\n\n    /**\n     * @dev Return the log in base 2 of a positive value rounded towards zero.\n     * Returns 0 if given 0.\n     */\n    function log2(uint256 value) internal pure returns (uint256) {\n        uint256 result = 0;\n        unchecked {\n            if (value >> 128 > 0) {\n                value >>= 128;\n                result += 128;\n            }\n            if (value >> 64 > 0) {\n                value >>= 64;\n                result += 64;\n            }\n            if (value >> 32 > 0) {\n                value >>= 32;\n                result += 32;\n            }\n            if (value >> 16 > 0) {\n                value >>= 16;\n                result += 16;\n            }\n            if (value >> 8 > 0) {\n                value >>= 8;\n                result += 8;\n            }\n            if (value >> 4 > 0) {\n                value >>= 4;\n                result += 4;\n            }\n            if (value >> 2 > 0) {\n                value >>= 2;\n                result += 2;\n            }\n            if (value >> 1 > 0) {\n                result += 1;\n            }\n        }\n        return result;\n    }\n\n    /**\n     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.\n     * Returns 0 if given 0.\n     */\n    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {\n        unchecked {\n            uint256 result = log2(value);\n            return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);\n        }\n    }\n\n    /**\n     * @dev Return the log in base 10 of a positive value rounded towards zero.\n     * Returns 0 if given 0.\n     */\n    function log10(uint256 value) internal pure returns (uint256) {\n        uint256 result = 0;\n        unchecked {\n            if (value >= 10 ** 64) {\n                value /= 10 ** 64;\n                result += 64;\n            }\n            if (value >= 10 ** 32) {\n                value /= 10 ** 32;\n                result += 32;\n            }\n            if (value >= 10 ** 16) {\n                value /= 10 ** 16;\n                result += 16;\n            }\n            if (value >= 10 ** 8) {\n                value /= 10 ** 8;\n                result += 8;\n            }\n            if (value >= 10 ** 4) {\n                value /= 10 ** 4;\n                result += 4;\n            }\n            if (value >= 10 ** 2) {\n                value /= 10 ** 2;\n                result += 2;\n            }\n            if (value >= 10 ** 1) {\n                result += 1;\n            }\n        }\n        return result;\n    }\n\n    /**\n     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.\n     * Returns 0 if given 0.\n     */\n    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {\n        unchecked {\n            uint256 result = log10(value);\n            return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);\n        }\n    }\n\n    /**\n     * @dev Return the log in base 256 of a positive value rounded towards zero.\n     * Returns 0 if given 0.\n     *\n     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.\n     */\n    function log256(uint256 value) internal pure returns (uint256) {\n        uint256 result = 0;\n        unchecked {\n            if (value >> 128 > 0) {\n                value >>= 128;\n                result += 16;\n            }\n            if (value >> 64 > 0) {\n                value >>= 64;\n                result += 8;\n            }\n            if (value >> 32 > 0) {\n                value >>= 32;\n                result += 4;\n            }\n            if (value >> 16 > 0) {\n                value >>= 16;\n                result += 2;\n            }\n            if (value >> 8 > 0) {\n                result += 1;\n            }\n        }\n        return result;\n    }\n\n    /**\n     * @dev Return the log in base 256, following the selected rounding direction, of a positive value.\n     * Returns 0 if given 0.\n     */\n    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {\n        unchecked {\n            uint256 result = log256(value);\n            return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);\n        }\n    }\n\n    /**\n     * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.\n     */\n    function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {\n        return uint8(rounding) % 2 == 1;\n    }\n}\n"},{"file_path":"@openzeppelin/contracts/utils/math/SignedMath.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SignedMath.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Standard signed math utilities missing in the Solidity language.\n */\nlibrary SignedMath {\n    /**\n     * @dev Returns the largest of two signed numbers.\n     */\n    function max(int256 a, int256 b) internal pure returns (int256) {\n        return a > b ? a : b;\n    }\n\n    /**\n     * @dev Returns the smallest of two signed numbers.\n     */\n    function min(int256 a, int256 b) internal pure returns (int256) {\n        return a < b ? a : b;\n    }\n\n    /**\n     * @dev Returns the average of two signed numbers without overflow.\n     * The result is rounded towards zero.\n     */\n    function average(int256 a, int256 b) internal pure returns (int256) {\n        // Formula from the book \"Hacker's Delight\"\n        int256 x = (a & b) + ((a ^ b) >> 1);\n        return x + (int256(uint256(x) >> 255) & (a ^ b));\n    }\n\n    /**\n     * @dev Returns the absolute unsigned value of a signed value.\n     */\n    function abs(int256 n) internal pure returns (uint256) {\n        unchecked {\n            // must be unchecked in order to support `n = type(int256).min`\n            return uint256(n >= 0 ? n : -n);\n        }\n    }\n}\n"},{"file_path":"contracts/LetterOfCreditStorage.sol","source_code":"// SPDX-License-Identifier: ISC\npragma solidity 0.8.25;\n\nimport \"./interfaces/ICollateral.sol\";\nimport \"./interfaces/ILetterOfCredit.sol\";\n\n/**\n * @title Contract specifying all ordered storage used by the existing LetterOfCredit contract proxy to avoid upgrade\n * storage collisions. All upgrade target candidates must inherit from this contract before any others.\n *\n * @dev These state variables were pulled verbatim from first LetterOfCredit contract pointed at by Anvil's\n * LetterOfCredit proxy.\n */\nabstract contract LetterOfCreditStorage {\n  /***********\n   * STORAGE *\n   ***********/\n\n  /// NB: uint96 stores up to 7.9 x 10^28 and packs tightly with addresses (12 + 20 = 32 bytes).\n  uint96 internal locNonce;\n\n  /// Max age of oracle update.\n  /// NB: uint16 gets us up to ~18hrs, which should be plenty. If our oracle is that stale we have very large problems.\n  uint16 public maxPriceUpdateSecondsAgo;\n\n  /// Extending a LOC can make it so that the total duration of any given LOC may be larger than this, but no LOC may\n  /// have more than this number of seconds remaining.\n  uint32 public maxLocDurationSeconds;\n\n  /// The ICollateral contract to use for new LOCs, after which, it is stored on the LOC referenced.\n  ICollateral public collateralContract;\n  // The IPriceOracle to use for all price interactions (NB: for both new and existing LOCs).\n  IPriceOracle public priceOracle;\n\n  /// id (nonce) => Letter of Credit\n  mapping(uint96 id => LOC letterOfCredit) internal locs;\n\n  /// Credited Token Address => token available for use as LOC credited tokens and its limits for use.\n  mapping(address creditedTokenAddress => CreditedToken creditedToken) internal creditedTokens;\n\n  /// collateral token address => credited token address => CollateralFactor.\n  mapping(address collateralTokenAddress => mapping(address creditedTokenAddress => CollateralFactor collateralFactor))\n    internal collateralToCreditedToCollateralFactors;\n\n  /*******************\n   * STORAGE STRUCTS *\n   *******************/\n\n  struct CreditedToken {\n    uint256 minPerDynamicLOC;\n    uint256 maxPerDynamicLOC;\n    uint256 globalMaxInDynamicUse;\n    uint256 globalAmountInDynamicUse;\n  }\n\n  struct CollateralFactor {\n    uint16 creationCollateralFactorBasisPoints;\n    uint16 collateralFactorBasisPoints;\n    uint16 liquidatorIncentiveBasisPoints;\n    /// The fee, in basis points, charged against a healthy LOC's collateral when it is liquidated\n    /// to serve a redemption. Unhealthy liquidations use liquidatorIncentiveBasisPoints instead.\n    /// @dev Appended in contract version 3.0.0. Safe: CollateralFactor is only ever a mapping\n    /// value, and the new member packs into the struct's existing (single) storage slot, so\n    /// pre-upgrade entries simply read 0 until re-upserted.\n    uint16 redeemBufferBasisPoints;\n  }\n\n  struct LOC {\n    uint96 collateralId;\n    address creator;\n    // --- storage slot separator\n    address beneficiary;\n    // NB: uint32 gets us to the year 2106. If we hit that, redeploy.\n    uint32 expirationTimestamp;\n    /// @dev DEPRECATED as of contract version 3.0.0: collateral factors are global (always read\n    /// from collateralToCreditedToCollateralFactors). Kept to preserve the storage layout of LOCs\n    /// created before V3; written as 0 for new LOCs and never read.\n    uint16 collateralFactorBasisPoints;\n    /// @dev DEPRECATED as of contract version 3.0.0. See collateralFactorBasisPoints above.\n    uint16 liquidatorIncentiveBasisPoints;\n    // --- storage slot separator\n    ICollateral collateralContract;\n    address collateralTokenAddress;\n    uint256 collateralTokenAmount;\n    uint256 claimableCollateral;\n    address creditedTokenAddress;\n    uint256 creditedTokenAmount;\n  }\n}\n"},{"file_path":"contracts/Pricing.sol","source_code":"// SPDX-License-Identifier: ISC\npragma solidity 0.8.25;\n\n/**\n * @title Library with often used math-related helper functions related to the Anvil protocol.\n *\n * @custom:security-contact security@af.xyz\n */\nlibrary Pricing {\n  error CastOverflow(uint256 input);\n\n  /// Example: human-readable price is 25000, {price: 25, exponent: 3, ...}\n  /// Example: human-readable price is 0.00004, {price: 4, exponent: -5, ...}\n  struct OraclePrice {\n    // Price\n    uint256 price;\n    // The exchange rate may be a decimal, but it will always be represented as a uint256.\n    // The price should be multiplied by 10**exponent to get the proper scale.\n    int32 exponent;\n    // Unix timestamp describing when the price was published\n    uint256 publishTime;\n  }\n\n  /**\n   * @notice Calculates the collateral factor implied by the provided amounts of collateral and credited tokens.\n   * @param _collateralTokenAmount The amount of the collateral token.\n   * @param _creditedTokenAmount The amount of the credited token.\n   * @param _price The price of the market in which the collateral is the input token and credited is the output token.\n   * @return The calculated collateral factor in basis points.\n   */\n  function collateralFactorInBasisPoints(\n    uint256 _collateralTokenAmount,\n    uint256 _creditedTokenAmount,\n    OraclePrice memory _price\n  ) internal pure returns (uint16) {\n    uint256 collateralInCredited = collateralAmountInCreditedToken(_collateralTokenAmount, _price);\n    // Don't divide by 0\n    if (collateralInCredited == 0) {\n      return type(uint16).max;\n    }\n\n    uint256 collateralFactor = (_creditedTokenAmount * 10_000) / collateralInCredited;\n    return collateralFactor > type(uint16).max ? type(uint16).max : uint16(collateralFactor);\n  }\n\n  /**\n   * @notice Calculates the amount of the credited token the provided collateral would yield, given the provided price.\n   * @param _collateralTokenAmount The amount of the collateral token.\n   * @param _price The price of the market in which the collateral is the input token and credited is the output token.\n   * @return _creditedTokenAmount The calculated amount of the credited token.\n   */\n  function collateralAmountInCreditedToken(\n    uint256 _collateralTokenAmount,\n    OraclePrice memory _price\n  ) internal pure returns (uint256) {\n    if (_price.exponent < 0) {\n      return (_collateralTokenAmount * _price.price) / (10 ** uint256(int256(-1 * _price.exponent)));\n    } else {\n      return _collateralTokenAmount * _price.price * (10 ** uint256(int256(_price.exponent)));\n    }\n  }\n\n  /**\n   * @notice Calculates the provided percentage of the provided amount.\n   * @param _amount The base amount for which the percentage will be calculated.\n   * @param _percentageBasisPoints The percentage, represented in basis points. For example, 10_000 is 100%.\n   * @return The resulting percentage.\n   */\n  function percentageOf(uint256 _amount, uint256 _percentageBasisPoints) internal pure returns (uint256) {\n    return (_amount * _percentageBasisPoints) / 10_000;\n  }\n\n  /**\n   * @notice Gets the result of the provided amount being increased by a relative fee.\n   * @dev This is the exact reverse of the `amountBeforeFee` function. Please note that calling one\n   * and then the other is not guaranteed to produce the starting value due to integer math.\n   * @param _amount The amount, to which the fee will be added.\n   * @param _feeBasisPoints The relative basis points value that amount should be increased by.\n   * @return The resulting amount with the relative fee applied.\n   */\n  function amountWithFee(uint256 _amount, uint16 _feeBasisPoints) internal pure returns (uint256) {\n    return _amount + percentageOf(_amount, uint256(_feeBasisPoints));\n  }\n\n  /**\n   * @notice Given an amount with a relative fee baked in, returns the amount before the fee was added.\n   * @dev This is the exact reverse of the `amountWithFee` function. Please note that calling one\n   * and then the other is not guaranteed to produce the starting value due to integer math.\n   * @param _amountWithFee The amount that includes the provided fee in its value.\n   * @param _feeBasisPoints The basis points value of the fee baked into the provided amount.\n   * @return The value of _amountWithFee before the _feeBasisPoints was added to it.\n   */\n  function amountBeforeFee(uint256 _amountWithFee, uint16 _feeBasisPoints) internal pure returns (uint256) {\n    return (_amountWithFee * 10_000) / (10_000 + _feeBasisPoints);\n  }\n\n  /**\n   * @dev Calculates the amount that is proportional to the provided fraction, given the denominator of the amount.\n   * For instance if a1/a2 = b1/b2, then b1 = calculateProportionOfTotal(a1, a2, b2).\n   * @param _aPortion The numerator of the reference proportion used to calculate the other numerator.\n   * @param _aTotal The numerator of the reference proportion used to calculate the other numerator.\n   * @param _bTotal The denominator for which we are calculating the numerator such that aPortion/aTotal = bPortion/bTotal.\n   * @param _bPortion The numerator that is an equal proportion of _bTotal that _aPortion is to _aTotal.\n   */\n  function calculateProportionOfTotal(\n    uint256 _aPortion,\n    uint256 _aTotal,\n    uint256 _bTotal\n  ) internal pure returns (uint256 _bPortion) {\n    if (_aTotal == 0) return 0;\n\n    // NB: It is a conscious choice to not catch overflows before they happen. This means that callers need to\n    // handle possible overflow reverts, but it saves gas for the great majority of cases.\n\n    // _bPortion / _bTotal = _aPortion / _aTotal;\n    // _bPortion = _bTotal * _aPortion / _aTotal\n    _bPortion = (_bTotal * _aPortion) / _aTotal;\n  }\n\n  /**\n   * @dev Safely casts the provided uint256 to an int256, reverting with CastOverflow on overflow.\n   * @param _input The input uint256 to cast.\n   * @return The safely casted uint256.\n   */\n  function safeCastToInt256(uint256 _input) internal pure returns (int256) {\n    if (_input > uint256(type(int256).max)) {\n      revert CastOverflow(_input);\n    }\n    return int256(_input);\n  }\n}\n"},{"file_path":"contracts/Refundable.sol","source_code":"// SPDX-License-Identifier: ISC\npragma solidity 0.8.25;\n\n/**\n * @title Base contract that can be extended to pull in `refundExcess` modifier, which ensures that the ETH balance of a\n * contract is not increased as a result of a function call.\n *\n * @custom:security-contact security@af.xyz\n */\nabstract contract Refundable {\n  /**\n   * @dev refunds excess ETH to the caller after an operation such that the contract's ETH balance cannot be increased\n   * as a result of the operation.\n   */\n  modifier refundExcess() {\n    uint256 startingBalance = address(this).balance;\n\n    _;\n\n    uint256 expectedEndingBalance = startingBalance - msg.value;\n    if (address(this).balance > expectedEndingBalance) {\n      payable(msg.sender).transfer(address(this).balance - expectedEndingBalance);\n    }\n  }\n}\n"},{"file_path":"contracts/SignatureNoncesUpgradeable.sol","source_code":"// SPDX-License-Identifier: ISC\npragma solidity 0.8.25;\n\nimport {Initializable} from \"@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol\";\n\n/**\n * @notice Builds off of \"@openzeppelin/contracts-upgradeable/utils/NoncesUpgradeable.sol\" by copying its code to make\n * the nonces more useful for signatures, namely:\n * - tracking nonces per account per operation rather than just per account\n * - allowing public nonce use by the account in question (e.g. for cancellation)\n *\n * @custom:security-contact security@af.xyz\n */\nabstract contract SignatureNoncesUpgradeable is Initializable {\n  /// @dev The nonce used for an `account` and `signatureType` is not the expected current nonce.\n  error InvalidNonce(address account, bytes32 signatureType, uint256 currentNonce);\n\n  /// @custom:storage-location erc7201:anvil.storage.SignatureNonces\n  struct SignatureNoncesStorage {\n    /// Stores nonces for the specified account and signature type.\n    mapping(address account => mapping(bytes32 signatureType => uint256 nonce)) _accountTypeNonces;\n  }\n\n  // keccak256(abi.encode(uint256(keccak256(\"anvil.storage.SignatureNonces\")) - 1)) & ~bytes32(uint256(0xff))\n  bytes32 private constant SignatureNoncesStorageLocation =\n    0xa10463db4f5369b35ba0ad7c2c4710776052e62babfb578b03cd47bf9aa4f100;\n\n  function _getSignatureNoncesStorage() private pure returns (SignatureNoncesStorage storage $) {\n    assembly {\n      $.slot := SignatureNoncesStorageLocation\n    }\n  }\n\n  function __Nonces_init() internal onlyInitializing {}\n\n  function __Nonces_init_unchained() internal onlyInitializing {}\n\n  /**\n   * @dev Returns the next unused nonce for an address and signature type.\n   */\n  function nonces(address _owner, bytes32 _signatureType) public view virtual returns (uint256) {\n    SignatureNoncesStorage storage $ = _getSignatureNoncesStorage();\n    return $._accountTypeNonces[_owner][_signatureType];\n  }\n\n  /**\n   * @dev Consumes a nonce.\n   *\n   * Returns the current value and increments nonce.\n   */\n  function _useNonce(address _owner, bytes32 _signatureType) internal virtual returns (uint256) {\n    SignatureNoncesStorage storage $ = _getSignatureNoncesStorage();\n    // For each account, the nonce has an initial value of 0, can only be incremented by one, and cannot be\n    // decremented or reset. This guarantees that the nonce never overflows.\n    unchecked {\n      // It is important to do x++ and not ++x here.\n      return $._accountTypeNonces[_owner][_signatureType]++;\n    }\n  }\n\n  /**\n   * @dev Same as {_useNonce} but checking that `nonce` is the next valid for `owner`.\n   */\n  function _useCheckedNonce(address _owner, bytes32 _signatureType, uint256 _nonce) internal virtual {\n    uint256 current = _useNonce(_owner, _signatureType);\n    if (_nonce != current) {\n      revert InvalidNonce(_owner, _signatureType, current);\n    }\n  }\n}\n"},{"file_path":"contracts/interfaces/ICollateral.sol","source_code":"// SPDX-License-Identifier: ISC\npragma solidity 0.8.25;\n\nimport \"./ICollateralDepositTarget.sol\";\n\n/**\n * @title The Collateral interface that must be exposed to make stored collateral useful to a Collateralizable contract.\n */\ninterface ICollateral is ICollateralDepositTarget {\n  /***************\n   * ERROR TYPES *\n   ***************/\n\n  error CollateralReservationNotFound(uint96 _id);\n  error ContractNotApprovedByProtocol(address _contract);\n  error ClaimAmountZero();\n  error ClaimableAmountZero();\n  error InsufficientAllowance(\n    address _contract,\n    address _accountAddress,\n    address _tokenAddress,\n    uint256 _need,\n    uint256 _have\n  );\n  error InsufficientCollateral(uint256 _need, uint256 _have);\n  error InvalidSignature(address _accountAddress);\n  error InvalidTargetAddress(address _address);\n  error InvalidUpgradeTarget(address _contract);\n  error InvalidZeroAmount();\n  error RelatedArraysLengthMismatch(uint256 _firstLength, uint256 _secondLength);\n  error TokenNotAllowed(address _address);\n  error Unauthorized(address _address);\n  error WithdrawalFeeTooHigh(uint16 _wouldBeValue, uint16 _max);\n\n  /**********\n   * EVENTS *\n   **********/\n\n  // common protocol events\n  event AccountCollateralizableContractAllowanceUpdated(\n    address indexed account,\n    address indexed contractAddress,\n    address indexed tokenAddress,\n    int256 modifiedByAmount,\n    uint256 newTotal\n  );\n  event AccountInitiatedUpgrade(\n    address indexed account,\n    address indexed toCollateralContract,\n    address[] tokenAddresses,\n    uint256[] amounts\n  );\n\n  event CollateralClaimed(\n    uint96 indexed reservationId,\n    uint256 amountWithFee,\n    uint256 feeAmount,\n    bool remainderReleased\n  );\n  event CollateralReleased(uint96 indexed reservationId, uint256 amount);\n  event CollateralReservationModified(\n    uint96 indexed reservationId,\n    uint256 oldAmount,\n    uint256 newAmount,\n    uint256 oldClaimableAmount,\n    uint256 newClaimableAmount\n  );\n  event CollateralReserved(\n    uint96 indexed reservationId,\n    address indexed account,\n    address reservingContract,\n    address tokenAddress,\n    uint256 amount,\n    uint256 claimableAmount,\n    uint16 claimFeeBasisPoints\n  );\n  event CollateralTransferred(\n    address indexed fromAccount,\n    address indexed tokenAddress,\n    address indexed toAccount,\n    uint256 tokenAmount\n  );\n\n  event FundsDeposited(address indexed from, address indexed toAccount, address tokenAddress, uint256 amount);\n  event FundsWithdrawn(\n    address indexed fromAccount,\n    address tokenAddress,\n    uint256 amountWithFee,\n    uint256 feeAmount,\n    address beneficiary\n  );\n\n  // governance events\n  event CollateralizableContractApprovalUpdated(bool approved, address contractAddress, bool isCollateralPool);\n  event CollateralTokenUpdated(bool enabled, address tokenAddress);\n  event CollateralUpgradeContractApprovalUpdated(bool approved, address upgradeContractAddress);\n  event ProtocolBalanceWithdrawn(address indexed destination, address[] tokenAddresses, uint256[] amounts);\n  event WithdrawalFeeUpdated(uint16 oldFeeBasisPoints, uint16 newFeeBasisPoints);\n\n  /***********\n   * STRUCTS *\n   ***********/\n\n  struct CollateralBalance {\n    uint256 available;\n    uint256 reserved;\n  }\n\n  struct CollateralToken {\n    // total deposits for all users for this token.\n    uint256 cumulativeUserBalance;\n    bool enabled;\n  }\n\n  struct CollateralReservation {\n    address collateralizableContract;\n    address account;\n    address tokenAddress;\n    uint16 feeBasisPoints;\n    uint256 tokenAmount;\n    uint256 claimableTokenAmount;\n  }\n\n  /*************\n   * FUNCTIONS *\n   *************/\n\n  /*** Views ***/\n\n  /**\n   * @notice Gets the CollateralToken with the provided address. If this collateral token does not exist, it will\n   * not revert but return a CollateralToken with default values for every field.\n   * @param _tokenAddress The address of the CollateralToken being fetched.\n   * @return _token The populated CollateralToken if found, empty otherwise.\n   */\n  function getCollateralToken(address _tokenAddress) external view returns (CollateralToken memory _token);\n\n  /**\n   * @notice Gets the CollateralBalance for the provided account and token.\n   * @param _accountAddress The account for which the CollateralBalance will be returned.\n   * @param _tokenAddress The address of the token for which the account's CollateralBalance will be returned.\n   * @return _balance The CollateralBalance for the account and token.\n   */\n  function getAccountCollateralBalance(\n    address _accountAddress,\n    address _tokenAddress\n  ) external view returns (CollateralBalance memory _balance);\n\n  /**\n   * @notice Gets the CollateralReservation for the provided ID.\n   * @dev NOTE: If a reservation does not exist for the provided ID, an empty CollateralReservation will be returned.\n   * @param _reservationId The ID of the CollateralReservation to be returned.\n   * @return _reservation The CollateralReservation.\n   */\n  function getCollateralReservation(\n    uint96 _reservationId\n  ) external view returns (CollateralReservation memory _reservation);\n\n  /**\n   * @notice Gets the claimable amount for the provided CollateralReservation ID.\n   * @dev NOTE: If a reservation does not exist for the provided ID, 0 will be returned.\n   * @param _reservationId The ID of the CollateralReservation to be returned.\n   * @return _claimable The claimable amount.\n   */\n  function getClaimableAmount(uint96 _reservationId) external view returns (uint256 _claimable);\n\n  /**\n   * @notice Gets amount of the account's assets in the provided token that the Collateralizable contract may use\n   * through this contract.\n   * @param _accountAddress The address of the account in question.\n   * @param _collateralizableContract The address of the Collateralizable contract.\n   * @param _tokenAddress The address of the token to which the allowance pertains.\n   * @return _allowance The allowance for the account-collateralizable-token combination. Note: If collateral is\n   * released, it is added to the allowance, so negative allowances are allowed to disable future collateral use.\n   */\n  function getCollateralizableTokenAllowance(\n    address _accountAddress,\n    address _collateralizableContract,\n    address _tokenAddress\n  ) external view returns (uint256 _allowance);\n\n  /**\n   * @notice Gets the fee for withdrawing funds from this vault, either directly or through claim.\n   * @return The fee in basis points.\n   */\n  function getWithdrawalFeeBasisPoints() external view returns (uint16);\n\n  /*** State-modifying functions ***/\n\n  /**\n   * @notice Claims reserved collateral, withdrawing it from the ICollateral contract.\n   * @dev The ICollateral contract will handle fee calculation and transfer _amountToReceive, supposing there is\n   * sufficient collateral reserved to cover _amountToReceive and the _reservationId's _claimFeeBasisPoints.\n   * @param _reservationId The ID of the collateral reservation in question.\n   * @param _amountToReceive The amount of collateral needed.\n   * @param _toAddress The address to which the `_amountToReceive` will be sent.\n   * @param _releaseRemainder Whether or not the remaining collateral should be released.\n   * Note: if the full amount is claimed, regardless of this value, the reservation is deleted.\n   * @return _remainingReservedCollateral The amount of collateral that remains reserved, if not released.\n   * @return _remainingClaimableCollateral The portion of the remaining collateral that may be claimed.\n   */\n  function claimCollateral(\n    uint96 _reservationId,\n    uint256 _amountToReceive,\n    address _toAddress,\n    bool _releaseRemainder\n  ) external returns (uint256 _remainingReservedCollateral, uint256 _remainingClaimableCollateral);\n\n  /**\n   * @notice Deposits the provided amount of the specified token into the specified account. Assets are sourced from\n   * the specified account's ERC-20 token balance.\n   *\n   * Note: Even if an account has previously approved a collateralizable to use its collateral, it must provide a\n   * deposit signature allowing it to deposit on its behalf. If the account-collateralizable allowance is less than\n   * the amount being deposited, the result of this call will be that the account-collateraliazble allowance is equal\n   * to the amount being deposited. If the allowance was already sufficient to use this newly deposited amount, the\n   * allowance will remain the same.\n   *\n   * @param _accountAddress The account address from which assets will be deposited and with which deposited assets will\n   * be associated in this contract.\n   * @param _tokenAddress The address of the token to be deposited.\n   * @param _amount The amount of the token to be deposited.\n   * @param _collateralizableDepositApprovalSignature Deposit approval signature permitting the calling collateralizable\n   * to deposit the account's collateral. This enables deposit-approve-and-use functionality in a single transaction.\n   */\n  function depositFromAccount(\n    address _accountAddress,\n    address _tokenAddress,\n    uint256 _amount,\n    bytes calldata _collateralizableDepositApprovalSignature\n  ) external;\n\n  /**\n   * @notice Modifies the amount of the calling account's assets the Collateralizable contract may use through this contract.\n   * @param _collateralizableContractAddress The address of the Collateralizable contract `msg.sender` is [dis]allowing.\n   * @param _tokenAddress The address of the token for which the allowance is being checked and updated.\n   * @param _byAmount The signed number by which the approved amount will be modified. Negative approved amounts\n   * function the same as 0 when attempting to reserve collateral. An account may choose to modify such that the allowance\n   * is negative since reservations, once released, add to the approved amount since those assets were previously approved.\n   */\n  function modifyCollateralizableTokenAllowance(\n    address _collateralizableContractAddress,\n    address _tokenAddress,\n    int256 _byAmount\n  ) external;\n\n  /**\n   * @notice Approves the provided collateralizable contract on behalf of the provided account address using the\n   * account's signature.\n   * @dev The signature is the EIP-712 signature formatted according to the following type hash variable:\n   * bytes32 public constant COLLATERALIZABLE_TOKEN_ALLOWANCE_ADJUSTMENT_TYPEHASH =\n   *  keccak256(\"CollateralizableTokenAllowanceAdjustment(address collateralizableAddress,address tokenAddress,int256 allowanceAdjustment,uint256 approverNonce)\");\n   *\n   * If this call is not successful, it will revert. If it succeeds, the caller may assume the modification succeeded.\n   * @param _accountAddress The account for which approval will take place.\n   * @param _collateralizableContractAddress The address of the collateralizable to approve.\n   * @param _allowanceAdjustment The allowance adjustment to approve. Note: this is a relative amount.\n   * @param _signature The signature to prove the account has authorized the approval.\n   */\n  function modifyCollateralizableTokenAllowanceWithSignature(\n    address _accountAddress,\n    address _collateralizableContractAddress,\n    address _tokenAddress,\n    int256 _allowanceAdjustment,\n    bytes calldata _signature\n  ) external;\n\n  /**\n   * @notice Adds/removes collateral to/from the reservation in question, leaving the reservation intact.\n   * @dev This call will revert if the modification is not successful.\n   * @param _reservationId The ID of the collateral reservation.\n   * @param _byAmount The amount by which the reservation will be modified (adding if positive, removing if negative).\n   * @return _reservedCollateral The total resulting reserved collateral.\n   * @return _claimableCollateral The total resulting claimable collateral.\n   */\n  function modifyCollateralReservation(\n    uint96 _reservationId,\n    int256 _byAmount\n  ) external returns (uint256 _reservedCollateral, uint256 _claimableCollateral);\n\n  /**\n   * @notice Pools assets from the provided account within the collateral contract into the calling Pool's account.\n   * This allows the caller to use assets from one or more accounts as a pool of assets.\n   * @dev This assumes the `_fromAccount` has given `msg.sender` permission to pool the provided amount of the token.\n   * @param _fromAccount The account from which collateral assets will be pooled.\n   * @param _tokenAddress The address of the token to pool.\n   * @param _tokensToPool The number of tokens to pool from the provided account.\n   */\n  function poolCollateral(address _fromAccount, address _tokenAddress, uint256 _tokensToPool) external;\n\n  /**\n   * @notice Releases all collateral from the reservation in question, releasing the reservation.\n   * @param _reservationId The ID of the collateral reservation.\n   * @return _totalCollateralReleased The collateral amount that was released.\n   */\n  function releaseAllCollateral(uint96 _reservationId) external returns (uint256 _totalCollateralReleased);\n\n  /**\n   * @notice Reserves collateral from the storing contract so that it may not be rehypothecated.\n   * @dev This call reserves the requisite amount of collateral such that the full `_amount` may be claimed. That is\n   * to say that `_amount` + `_claimFeeBasisPoints` will actually be reserved.\n   * @param _accountAddress The address of the account whose assets are being reserved.\n   * @param _tokenAddress The address of the Token being reserved as collateral.\n   * @param _claimableAmount The amount of the Token that must be claimable.\n   * @return _reservationId The ID that can be used to refer to this reservation when claiming or releasing collateral.\n   * @return _totalAmountReserved The total amount reserved from the account in question.\n   */\n  function reserveClaimableCollateral(\n    address _accountAddress,\n    address _tokenAddress,\n    uint256 _claimableAmount\n  ) external returns (uint96 _reservationId, uint256 _totalAmountReserved);\n\n  /**\n   * @notice Reserves collateral from the storing contract so that it may not be rehypothecated.\n   * @dev Note that the full _amount reserved will not be received when claimed due to _claimFeeBasisPoints. Supposing\n   * the whole amount is claimed, _amount * (1000 - _claimFeeBasisPoints) / 1000 will be received if claimed.\n   * @param _accountAddress The address of the account whose assets are being reserved.\n   * @param _tokenAddress The address of the Token being reserved as collateral.\n   * @param _amount The amount of the Token being reserved as collateral.\n   * @return _reservationId The ID that can be used to refer to this reservation when claiming or releasing collateral.\n   * @return _claimableCollateral The collateral that may be claimed (factoring in the withdrawal fee).\n   */\n  function reserveCollateral(\n    address _accountAddress,\n    address _tokenAddress,\n    uint256 _amount\n  ) external returns (uint96 _reservationId, uint256 _claimableCollateral);\n\n  /**\n   * @notice Transfers the provided amount of the caller's available collateral to the provided destination address.\n   * @param _tokenAddress The address of the collateral token being transferred.\n   * @param _amount The number of collateral tokens being transferred.\n   * @param _destinationAddress The address of the account to which assets will be released.\n   */\n  function transferCollateral(address _tokenAddress, uint256 _amount, address _destinationAddress) external;\n\n  /**\n   * @notice Withdraws an ERC-20 token from this `Collateral` vault to the provided address on behalf of the sender,\n   * provided the requester has sufficient available balance.\n   * @notice There is a protocol fee for withdrawals, so a successful withdrawal of `_amount` will entail the\n   * account's balance being lowered by `_amount`, but the `_destination` address receiving `_amount` less the fee.\n   * @param _tokenAddress The token address of the ERC-20 token to withdraw.\n   * @param _amount The amount of the ERC-20 token to withdraw.\n   * @param _destinationAddress The address that will receive the assets. Note: cannot be 0.\n   */\n  function withdraw(address _tokenAddress, uint256 _amount, address _destinationAddress) external;\n}\n"},{"file_path":"contracts/interfaces/ICollateralDepositTarget.sol","source_code":"// SPDX-License-Identifier: ISC\npragma solidity 0.8.25;\n\n/**\n * @title An interface allowing the deposit of assets into a new ICollateral contract for benefit of a specified account.\n * @dev This function may be used to transfer account assets from one ICollateral contract to another as an upgrade.\n */\ninterface ICollateralDepositTarget {\n  /**\n   * @notice Deposits assets from the calling contract into the implementing target on behalf of users.\n   * @dev The calling contract should iterate and approve _amounts of all Tokens in _tokenAddresses to be transferred\n   * by the implementing contract.\n   * @dev The implementing contract MUST iterate and transfer each of the Tokens in _tokenAddresses and transfer the\n   * _amounts to itself from the calling contract or revert if that is not possible.\n   * @param _accountAddress The address of the account to be credited assets in the implementing contract.\n   * @param _tokenAddresses The list of addresses of the Tokens to transfer. Indexes must correspond to _amounts.\n   * @param _amounts The list of amounts of the Tokens to transfer. Indexes must correspond to _tokenAddresses.\n   */\n  function depositToAccount(\n    address _accountAddress,\n    address[] calldata _tokenAddresses,\n    uint256[] calldata _amounts\n  ) external;\n}\n"},{"file_path":"contracts/interfaces/ILetterOfCredit.sol","source_code":"// SPDX-License-Identifier: ISC\npragma solidity 0.8.25;\n\nimport \"./IPriceOracle.sol\";\n\n/**\n * @title Interface for LetterOfCredit contract that simply contains events and errors for readability rather than\n * defining an interaction interface.\n */\ninterface ILetterOfCredit {\n  /***************\n   * ERROR TYPES *\n   ***************/\n\n  error NoOp();\n  error LOCNotFound(uint96 _id);\n  error AddressUnauthorizedForLOC(address _address, uint96 _forID);\n  error PriceUpdateStale(uint32 _publishTimeSeconds, uint16 _maxPriceUpdateSecondsAgo);\n  error InvalidSignature(address _accountAddress);\n  error InvalidLOCParameters();\n  error InvalidRedeemAmount(uint256 _requestedAmount, uint256 _maxAvailable);\n  error InvalidZeroAddress();\n  error InvalidUpgradeContract();\n  error MaxLOCDurationExceeded(uint32 _maxSeconds, uint32 _expirationTimestampSeconds);\n  error InsufficientCollateral(uint256 _need, uint256 _have);\n  error AssetPairUnauthorized(address _collateralToken, address _creditedToken);\n  error InvalidCollateralFactor(uint16 _maxBasisPoints, uint16 _basisPoints);\n  error CollateralFactorOverlap(uint16 _creationCollateralFactor, uint16 _liquidationCollateralFactor);\n  error ConversionFundsReceivedMismatch(uint256 _expectedFundsReceived, uint256 _actualFundsReceived);\n  error CollateralAmountInCreditedTokenZero();\n  error PartialRedeemInsolvent();\n  error PartialConvertWithoutRedeem(uint96 _id);\n  error InvalidBasisPointValue(uint16 _value);\n  error LiquidatorIncentiveAboveMax(uint16 _max, uint16 _value);\n  error LiquidatorIncentiveChanged(uint16 _was, uint16 _is);\n  error InvalidMaxPriceUpdateSecondsAgo(uint16 _min, uint16 _max, uint16 _value);\n  error InvalidLOCExtensionTimestamp(uint96 _id, uint32 _newExpirationTimestampSeconds);\n  error CreditedTokenMinMaxOverlap();\n  error EnabledCreditedTokenMinPerDynamicLOCZero();\n  error LOCCreditedTokenMaxExceeded(uint256 _maxPerLOC, uint256 _value);\n  error LOCCreditedTokenUnderMinimum(uint256 _minPerLOC, uint256 _value);\n  error GlobalCreditedTokenMaxInUseExceeded(uint256 _globalMaxInUse, uint256 _value);\n  error LOCExpired(uint96 _id, uint32 _expirationTimestampSeconds);\n  error LOCAlreadyConverted(uint96 _id);\n  error LiquidationAmountTooSmall(uint256 _collateralToSendLiquidator, uint256 _creditedAmountToReceive);\n  error TagTooLong(uint256 _maxBytes, uint256 _actualBytes);\n  error RedeemBufferAboveMax(uint16 _max, uint16 _value);\n  error LOCHealthy(uint96 _id);\n\n  /**********\n   * EVENTS *\n   **********/\n\n  /**\n   * Emitted when a LOC is created (contract version 3.0.0+).\n   * @dev Differences from LOCCreatedV2: `tag` is dynamic-length bytes and intentionally NOT indexed\n   * (indexing a dynamic type would emit only its keccak256 hash, hiding the value from indexers),\n   * and the per-LOC collateralFactorBasisPoints / liquidatorIncentiveBasisPoints snapshots are gone\n   * because collateral factors are global as of V3 (read the current CollateralFactor config).\n   */\n  event LOCCreatedV3(\n    address indexed creator,\n    address indexed beneficiary,\n    bytes tag,\n    address collateralContractAddress,\n    address collateralTokenAddress,\n    uint256 collateralTokenAmount,\n    uint256 claimableCollateral,\n    uint32 expirationTimestamp,\n    address creditedTokenAddress,\n    uint256 creditedTokenAmount,\n    uint96 id,\n    uint96 collateralId\n  );\n\n  /**\n   * Deprecated in favor of LOCCreatedV3. Emitted by the V2 implementation; must remain declared for\n   * backward compatibility (historical event decoding and the archived LetterOfCreditV2 contract).\n   */\n  event LOCCreatedV2(\n    address indexed creator,\n    address indexed beneficiary,\n    bytes32 indexed tag,\n    address collateralContractAddress,\n    address collateralTokenAddress,\n    uint256 collateralTokenAmount,\n    uint256 claimableCollateral,\n    uint32 expirationTimestamp,\n    uint16 collateralFactorBasisPoints,\n    uint16 liquidatorIncentiveBasisPoints,\n    address creditedTokenAddress,\n    uint256 creditedTokenAmount,\n    uint96 id,\n    uint96 collateralId\n  );\n\n  /**\n   * Deprecated. Some of these events were emitted prior to the proxy upgrade that moved to using LOCCreatedV2, so\n   * this event must exist for backward compatibility.\n   */\n  event LOCCreated(\n    address indexed creator,\n    address indexed beneficiary,\n    address collateralContractAddress,\n    address collateralTokenAddress,\n    uint256 collateralTokenAmount,\n    uint256 claimableCollateral,\n    uint32 expirationTimestamp,\n    uint16 collateralFactorBasisPoints,\n    uint16 liquidatorIncentiveBasisPoints,\n    address creditedTokenAddress,\n    uint256 creditedTokenAmount,\n    uint96 id\n  );\n\n  event LOCCanceled(uint96 indexed id);\n\n  event LOCExtended(uint96 indexed id, uint32 oldExpirationTimestamp, uint32 newExpirationTimestamp);\n\n  event LOCConverted(\n    uint96 indexed id,\n    address indexed initiator,\n    address indexed liquidator,\n    uint256 liquidationAmount,\n    uint256 liquidationFeeAmount,\n    uint256 creditedTokenAmountReceived\n  );\n\n  // NB: Exact same args as LOCConverted, but we don't want to emit LOCConverted when the entire LOC is not converted.\n  event LOCPartiallyLiquidated(\n    uint96 indexed id,\n    address indexed initiator,\n    address indexed liquidator,\n    uint256 liquidationAmount,\n    uint256 liquidationFeeAmount,\n    uint256 creditedTokenAmountReceived\n  );\n\n  event LOCRedeemed(\n    uint96 indexed id,\n    address indexed destinationAddress,\n    uint256 creditedTokenAmount,\n    uint256 collateralTokenAmountUsed,\n    uint256 claimableCollateralUsed\n  );\n\n  event LOCCollateralModified(\n    uint96 indexed id,\n    uint256 oldCollateralAmount,\n    uint256 newCollateralAmount,\n    uint256 newClaimableCollateral\n  );\n\n  event CreditedTokenUpdated(\n    address indexed tokenAddress,\n    uint256 minPerDynamicLOC,\n    uint256 maxPerDynamicLOC,\n    uint256 globalMaxInDynamicUse\n  );\n\n  /**\n   * Emitted when an asset pair's collateral factors are upserted (contract version 3.0.0+).\n   * Supersedes CollateralFactorUpdated, adding redeemBufferBasisPoints.\n   */\n  event CollateralFactorUpdatedV2(\n    address indexed collateralTokenAddress,\n    address indexed creditedTokenAddress,\n    uint16 creationCollateralFactorBasisPoints,\n    uint16 collateralFactorBasisPoints,\n    uint16 liquidatorIncentiveBasisPoints,\n    uint16 redeemBufferBasisPoints\n  );\n\n  /**\n   * Deprecated in favor of CollateralFactorUpdatedV2. Emitted by pre-V3 implementations; must\n   * remain declared for backward compatibility (historical event decoding and the archived\n   * LetterOfCreditV2 contract).\n   */\n  event CollateralFactorUpdated(\n    address indexed collateralTokenAddress,\n    address indexed creditedTokenAddress,\n    uint16 creationCollateralFactorBasisPoints,\n    uint16 collateralFactorBasisPoints,\n    uint16 liquidatorIncentiveBasisPoints\n  );\n\n  event MaxPriceUpdateSecondsAgoUpdated(uint16 oldSecondsAgo, uint16 newSecondsAgo);\n  event MaxLocDurationSecondsUpdated(uint32 oldMaxDurationSeconds, uint32 newMaxDurationSeconds);\n  event CollateralAddressUpgraded(address oldCollateralAddress, address newCollateralAddress);\n\n  event PriceOracleUpgradeRevoked();\n  event PriceOracleUpgradePending(IPriceOracle priceOracle, uint256 validAfterTimestamp);\n  event PriceOracleUpgraded(IPriceOracle oldPriceOracle, IPriceOracle newPriceOracle);\n\n  event OracleTimeDelayUpdatePending(uint256 timeDelay, uint256 validAfterTimestamp);\n  event OracleTimeDelayUpdated(uint256 oldTimeDelay, uint256 newTimeDelay);\n}\n"},{"file_path":"contracts/interfaces/ILiquidatable.sol","source_code":"// SPDX-License-Identifier: ISC\npragma solidity 0.8.25;\n\ninterface ILiquidatable {\n  /**\n   * @notice Liquidates the collateral from the provided LOC. As of LetterOfCredit V3 this may only\n   * be called for UNHEALTHY LOCs (healthy LOCs are liquidated exclusively through redemption).\n   * @dev If _liquidatorToUse is populated, liquidation will occur through the ILiquidator interface callback. If not,\n   * liquidation will be attempted by implementing contract transferring the required amount of credited tokens from\n   * `msg.sender`.\n   *\n   * @param _locId The ID of the unhealthy LOC to liquidate.\n   * @param _iLiquidatorToUse (optional) The ILiquidator to use for liquidation if not liquidating directly from the\n   * assets of `msg.sender`.\n   * @param _oraclePriceUpdate (optional) The oracle price update to be used for this liquidation.\n   * @param _creatorAuthorization DEPRECATED and ignored as of LetterOfCredit V3 (creators no longer\n   * have voluntary-conversion rights, so there is nothing to authorize). Retained so the ABI\n   * existing liquidation infrastructure depends on is unchanged; pass empty bytes.\n   * @param _liquidatorParams (optional) Parameters to be parsed and used by the ILiquidator implementation contract.\n   */\n  function convertLOC(\n    uint96 _locId,\n    address _iLiquidatorToUse,\n    bytes calldata _oraclePriceUpdate,\n    bytes calldata _creatorAuthorization,\n    bytes calldata _liquidatorParams\n  ) external payable;\n}\n"},{"file_path":"contracts/interfaces/ILiquidator.sol","source_code":"// SPDX-License-Identifier: ISC\npragma solidity 0.8.25;\n\ninterface ILiquidator {\n  /**\n   * @notice Called to provide input token in exchange for output token in the specified amounts.\n   * @dev It is assumed that caller has approved the Liquidator to transfer the `_inputTokenAmount`.\n   * @dev At a minimum, the implementer must send output token to `initiator`.\n   * @param _initiator The original initiator of the liquidation, if necessary for payment by the ILiquidator.\n   * @param _inputTokenAddress The address of the token the liquidator will receive from the caller.\n   * @param _inputTokenAmount The amount of the token the liquidator will receive from the caller.\n   * @param _outputTokenAddress The address of the token the caller will receive as a result of this call.\n   * @param _outputTokenAmount The amount of the token the caller will receive as a result of this call.\n   * @param _liquidatorParams Parameters, if any, to be parsed and used by the ILiquidator implementation contract.\n   */\n  function liquidate(\n    address _initiator,\n    address _inputTokenAddress,\n    uint256 _inputTokenAmount,\n    address _outputTokenAddress,\n    uint256 _outputTokenAmount,\n    bytes calldata _liquidatorParams\n  ) external;\n}\n"},{"file_path":"contracts/interfaces/IPriceOracle.sol","source_code":"// SPDX-License-Identifier: ISC\npragma solidity 0.8.25;\n\nimport \"../Pricing.sol\";\n\n/**\n * @title Defines the interface that PriceOracles must implement to be used within the LOC ecosystem.\n */\ninterface IPriceOracle {\n  /**\n   * @notice Gets the existing price to trade the provided input token for the provided output token.\n   * @param _inputTokenAddress The address of the token to be [hypothetically] sent in a trade.\n   * @param _outputTokenAddress The address of the token to be [hypothetically] received in a trade.\n   * @return _price The `OraclePrice` for the specified trading pair.\n   */\n  function getPrice(\n    address _inputTokenAddress,\n    address _outputTokenAddress\n  ) external returns (Pricing.OraclePrice memory _price);\n\n  /*\n   * @notice Pushes an oracle price update to the oracle update logic for a trading pair, returning updated price.\n   * @dev Under the hood, return price should come from `getPrice(...)` to ensure that this returned price _always_\n   * matches the price that a caller would get by immediately calling `getPrice(...)` after update.\n   * @dev If the update is invalid or does not work for some reason, the transaction should revert, except in the case\n   * in which a newer price already exists. In that case, the update should succeed as a no-op.\n   * @param _inputTokenAddress The address of the token to be [hypothetically] sent in a trade.\n   * @param _outputTokenAddress The address of the token to be [hypothetically] received in a trade.\n   * @param _oracleData The oracle price data necessary for the implementation to verify content and update price.\n   * @return _price The updated price set by the `_oracleData`.\n   */\n  function updatePrice(\n    address _inputTokenAddress,\n    address _outputTokenAddress,\n    bytes calldata _oracleData\n  ) external payable returns (Pricing.OraclePrice memory _price);\n\n  /**\n   * @notice Gets the fee required to use the provided `_oracleData` in a call to `updatePrice`.\n   * @param _oracleData The oracle data bytes that may be passed to updatePrice.\n   * @return _feeAmount The fee that must be passed as msg.value to `updatePrice` to submit the provided oracle data.\n   */\n  function getUpdateFee(bytes calldata _oracleData) external view returns (uint256 _feeAmount);\n}\n"}],"certified":false,"conflicting_implementations":null,"abi":[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[{"internalType":"address","name":"_address","type":"address"},{"internalType":"uint96","name":"_forID","type":"uint96"}],"name":"AddressUnauthorizedForLOC","type":"error"},{"inputs":[{"internalType":"address","name":"_collateralToken","type":"address"},{"internalType":"address","name":"_creditedToken","type":"address"}],"name":"AssetPairUnauthorized","type":"error"},{"inputs":[{"internalType":"uint256","name":"input","type":"uint256"}],"name":"CastOverflow","type":"error"},{"inputs":[],"name":"CollateralAmountInCreditedTokenZero","type":"error"},{"inputs":[{"internalType":"uint16","name":"_creationCollateralFactor","type":"uint16"},{"internalType":"uint16","name":"_liquidationCollateralFactor","type":"uint16"}],"name":"CollateralFactorOverlap","type":"error"},{"inputs":[{"internalType":"uint256","name":"_expectedFundsReceived","type":"uint256"},{"internalType":"uint256","name":"_actualFundsReceived","type":"uint256"}],"name":"ConversionFundsReceivedMismatch","type":"error"},{"inputs":[],"name":"CreditedTokenMinMaxOverlap","type":"error"},{"inputs":[],"name":"EnabledCreditedTokenMinPerDynamicLOCZero","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[{"internalType":"uint256","name":"_globalMaxInUse","type":"uint256"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"GlobalCreditedTokenMaxInUseExceeded","type":"error"},{"inputs":[{"internalType":"uint256","name":"_need","type":"uint256"},{"internalType":"uint256","name":"_have","type":"uint256"}],"name":"InsufficientCollateral","type":"error"},{"inputs":[{"internalType":"uint16","name":"_value","type":"uint16"}],"name":"InvalidBasisPointValue","type":"error"},{"inputs":[{"internalType":"uint16","name":"_maxBasisPoints","type":"uint16"},{"internalType":"uint16","name":"_basisPoints","type":"uint16"}],"name":"InvalidCollateralFactor","type":"error"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[{"internalType":"uint96","name":"_id","type":"uint96"},{"internalType":"uint32","name":"_newExpirationTimestampSeconds","type":"uint32"}],"name":"InvalidLOCExtensionTimestamp","type":"error"},{"inputs":[],"name":"InvalidLOCParameters","type":"error"},{"inputs":[{"internalType":"uint16","name":"_min","type":"uint16"},{"internalType":"uint16","name":"_max","type":"uint16"},{"internalType":"uint16","name":"_value","type":"uint16"}],"name":"InvalidMaxPriceUpdateSecondsAgo","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"bytes32","name":"signatureType","type":"bytes32"},{"internalType":"uint256","name":"currentNonce","type":"uint256"}],"name":"InvalidNonce","type":"error"},{"inputs":[{"internalType":"uint256","name":"_requestedAmount","type":"uint256"},{"internalType":"uint256","name":"_maxAvailable","type":"uint256"}],"name":"InvalidRedeemAmount","type":"error"},{"inputs":[{"internalType":"address","name":"_accountAddress","type":"address"}],"name":"InvalidSignature","type":"error"},{"inputs":[],"name":"InvalidUpgradeContract","type":"error"},{"inputs":[],"name":"InvalidZeroAddress","type":"error"},{"inputs":[{"internalType":"uint96","name":"_id","type":"uint96"}],"name":"LOCAlreadyConverted","type":"error"},{"inputs":[{"internalType":"uint256","name":"_maxPerLOC","type":"uint256"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"LOCCreditedTokenMaxExceeded","type":"error"},{"inputs":[{"internalType":"uint256","name":"_minPerLOC","type":"uint256"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"LOCCreditedTokenUnderMinimum","type":"error"},{"inputs":[{"internalType":"uint96","name":"_id","type":"uint96"},{"internalType":"uint32","name":"_expirationTimestampSeconds","type":"uint32"}],"name":"LOCExpired","type":"error"},{"inputs":[{"internalType":"uint96","name":"_id","type":"uint96"}],"name":"LOCHealthy","type":"error"},{"inputs":[{"internalType":"uint96","name":"_id","type":"uint96"}],"name":"LOCNotFound","type":"error"},{"inputs":[{"internalType":"uint256","name":"_collateralToSendLiquidator","type":"uint256"},{"internalType":"uint256","name":"_creditedAmountToReceive","type":"uint256"}],"name":"LiquidationAmountTooSmall","type":"error"},{"inputs":[{"internalType":"uint16","name":"_max","type":"uint16"},{"internalType":"uint16","name":"_value","type":"uint16"}],"name":"LiquidatorIncentiveAboveMax","type":"error"},{"inputs":[{"internalType":"uint16","name":"_was","type":"uint16"},{"internalType":"uint16","name":"_is","type":"uint16"}],"name":"LiquidatorIncentiveChanged","type":"error"},{"inputs":[{"internalType":"uint32","name":"_maxSeconds","type":"uint32"},{"internalType":"uint32","name":"_expirationTimestampSeconds","type":"uint32"}],"name":"MaxLOCDurationExceeded","type":"error"},{"inputs":[],"name":"NoOp","type":"error"},{"inputs":[],"name":"NotInitializing","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[{"internalType":"uint96","name":"_id","type":"uint96"}],"name":"PartialConvertWithoutRedeem","type":"error"},{"inputs":[],"name":"PartialRedeemInsolvent","type":"error"},{"inputs":[{"internalType":"uint32","name":"_publishTimeSeconds","type":"uint32"},{"internalType":"uint16","name":"_maxPriceUpdateSecondsAgo","type":"uint16"}],"name":"PriceUpdateStale","type":"error"},{"inputs":[{"internalType":"uint16","name":"_max","type":"uint16"},{"internalType":"uint16","name":"_value","type":"uint16"}],"name":"RedeemBufferAboveMax","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[{"internalType":"uint256","name":"_maxBytes","type":"uint256"},{"internalType":"uint256","name":"_actualBytes","type":"uint256"}],"name":"TagTooLong","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldCollateralAddress","type":"address"},{"indexed":false,"internalType":"address","name":"newCollateralAddress","type":"address"}],"name":"CollateralAddressUpgraded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"collateralTokenAddress","type":"address"},{"indexed":true,"internalType":"address","name":"creditedTokenAddress","type":"address"},{"indexed":false,"internalType":"uint16","name":"creationCollateralFactorBasisPoints","type":"uint16"},{"indexed":false,"internalType":"uint16","name":"collateralFactorBasisPoints","type":"uint16"},{"indexed":false,"internalType":"uint16","name":"liquidatorIncentiveBasisPoints","type":"uint16"}],"name":"CollateralFactorUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"collateralTokenAddress","type":"address"},{"indexed":true,"internalType":"address","name":"creditedTokenAddress","type":"address"},{"indexed":false,"internalType":"uint16","name":"creationCollateralFactorBasisPoints","type":"uint16"},{"indexed":false,"internalType":"uint16","name":"collateralFactorBasisPoints","type":"uint16"},{"indexed":false,"internalType":"uint16","name":"liquidatorIncentiveBasisPoints","type":"uint16"},{"indexed":false,"internalType":"uint16","name":"redeemBufferBasisPoints","type":"uint16"}],"name":"CollateralFactorUpdatedV2","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"tokenAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"minPerDynamicLOC","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"maxPerDynamicLOC","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"globalMaxInDynamicUse","type":"uint256"}],"name":"CreditedTokenUpdated","type":"event"},{"anonymous":false,"inputs":[],"name":"EIP712DomainChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint96","name":"id","type":"uint96"}],"name":"LOCCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint96","name":"id","type":"uint96"},{"indexed":false,"internalType":"uint256","name":"oldCollateralAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newCollateralAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newClaimableCollateral","type":"uint256"}],"name":"LOCCollateralModified","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint96","name":"id","type":"uint96"},{"indexed":true,"internalType":"address","name":"initiator","type":"address"},{"indexed":true,"internalType":"address","name":"liquidator","type":"address"},{"indexed":false,"internalType":"uint256","name":"liquidationAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"liquidationFeeAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"creditedTokenAmountReceived","type":"uint256"}],"name":"LOCConverted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"creator","type":"address"},{"indexed":true,"internalType":"address","name":"beneficiary","type":"address"},{"indexed":false,"internalType":"address","name":"collateralContractAddress","type":"address"},{"indexed":false,"internalType":"address","name":"collateralTokenAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"collateralTokenAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"claimableCollateral","type":"uint256"},{"indexed":false,"internalType":"uint32","name":"expirationTimestamp","type":"uint32"},{"indexed":false,"internalType":"uint16","name":"collateralFactorBasisPoints","type":"uint16"},{"indexed":false,"internalType":"uint16","name":"liquidatorIncentiveBasisPoints","type":"uint16"},{"indexed":false,"internalType":"address","name":"creditedTokenAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"creditedTokenAmount","type":"uint256"},{"indexed":false,"internalType":"uint96","name":"id","type":"uint96"}],"name":"LOCCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"creator","type":"address"},{"indexed":true,"internalType":"address","name":"beneficiary","type":"address"},{"indexed":true,"internalType":"bytes32","name":"tag","type":"bytes32"},{"indexed":false,"internalType":"address","name":"collateralContractAddress","type":"address"},{"indexed":false,"internalType":"address","name":"collateralTokenAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"collateralTokenAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"claimableCollateral","type":"uint256"},{"indexed":false,"internalType":"uint32","name":"expirationTimestamp","type":"uint32"},{"indexed":false,"internalType":"uint16","name":"collateralFactorBasisPoints","type":"uint16"},{"indexed":false,"internalType":"uint16","name":"liquidatorIncentiveBasisPoints","type":"uint16"},{"indexed":false,"internalType":"address","name":"creditedTokenAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"creditedTokenAmount","type":"uint256"},{"indexed":false,"internalType":"uint96","name":"id","type":"uint96"},{"indexed":false,"internalType":"uint96","name":"collateralId","type":"uint96"}],"name":"LOCCreatedV2","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"creator","type":"address"},{"indexed":true,"internalType":"address","name":"beneficiary","type":"address"},{"indexed":false,"internalType":"bytes","name":"tag","type":"bytes"},{"indexed":false,"internalType":"address","name":"collateralContractAddress","type":"address"},{"indexed":false,"internalType":"address","name":"collateralTokenAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"collateralTokenAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"claimableCollateral","type":"uint256"},{"indexed":false,"internalType":"uint32","name":"expirationTimestamp","type":"uint32"},{"indexed":false,"internalType":"address","name":"creditedTokenAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"creditedTokenAmount","type":"uint256"},{"indexed":false,"internalType":"uint96","name":"id","type":"uint96"},{"indexed":false,"internalType":"uint96","name":"collateralId","type":"uint96"}],"name":"LOCCreatedV3","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint96","name":"id","type":"uint96"},{"indexed":false,"internalType":"uint32","name":"oldExpirationTimestamp","type":"uint32"},{"indexed":false,"internalType":"uint32","name":"newExpirationTimestamp","type":"uint32"}],"name":"LOCExtended","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint96","name":"id","type":"uint96"},{"indexed":true,"internalType":"address","name":"initiator","type":"address"},{"indexed":true,"internalType":"address","name":"liquidator","type":"address"},{"indexed":false,"internalType":"uint256","name":"liquidationAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"liquidationFeeAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"creditedTokenAmountReceived","type":"uint256"}],"name":"LOCPartiallyLiquidated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint96","name":"id","type":"uint96"},{"indexed":true,"internalType":"address","name":"destinationAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"creditedTokenAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"collateralTokenAmountUsed","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"claimableCollateralUsed","type":"uint256"}],"name":"LOCRedeemed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"oldMaxDurationSeconds","type":"uint32"},{"indexed":false,"internalType":"uint32","name":"newMaxDurationSeconds","type":"uint32"}],"name":"MaxLocDurationSecondsUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint16","name":"oldSecondsAgo","type":"uint16"},{"indexed":false,"internalType":"uint16","name":"newSecondsAgo","type":"uint16"}],"name":"MaxPriceUpdateSecondsAgoUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"timeDelay","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"validAfterTimestamp","type":"uint256"}],"name":"OracleTimeDelayUpdatePending","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldTimeDelay","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newTimeDelay","type":"uint256"}],"name":"OracleTimeDelayUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract IPriceOracle","name":"priceOracle","type":"address"},{"indexed":false,"internalType":"uint256","name":"validAfterTimestamp","type":"uint256"}],"name":"PriceOracleUpgradePending","type":"event"},{"anonymous":false,"inputs":[],"name":"PriceOracleUpgradeRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract IPriceOracle","name":"oldPriceOracle","type":"address"},{"indexed":false,"internalType":"contract IPriceOracle","name":"newPriceOracle","type":"address"}],"name":"PriceOracleUpgraded","type":"event"},{"inputs":[],"name":"CANCEL_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_TAG_BYTES","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"REDEEM_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint96","name":"_locId","type":"uint96"},{"internalType":"bytes","name":"_beneficiaryAuthorization","type":"bytes"}],"name":"cancelLOC","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"collateralContract","outputs":[{"internalType":"contract ICollateral","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint96","name":"_locId","type":"uint96"},{"internalType":"address","name":"_liquidatorToUse","type":"address"},{"internalType":"bytes","name":"_oraclePriceUpdate","type":"bytes"},{"internalType":"bytes","name":"","type":"bytes"},{"internalType":"bytes","name":"_liquidatorParams","type":"bytes"}],"name":"convertLOC","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_beneficiary","type":"address"},{"internalType":"address","name":"_collateralTokenAddress","type":"address"},{"internalType":"uint256","name":"_collateralTokenAmount","type":"uint256"},{"internalType":"address","name":"_creditedTokenAddress","type":"address"},{"internalType":"uint256","name":"_creditedTokenAmount","type":"uint256"},{"internalType":"uint32","name":"_expirationTimestamp","type":"uint32"},{"internalType":"bytes","name":"_oraclePriceUpdate","type":"bytes"},{"internalType":"bytes","name":"_collateralizableAllowanceSignature","type":"bytes"},{"internalType":"bytes","name":"_tag","type":"bytes"}],"name":"createDynamicLOC","outputs":[{"internalType":"uint96","name":"","type":"uint96"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_beneficiary","type":"address"},{"internalType":"address","name":"_tokenAddress","type":"address"},{"internalType":"uint256","name":"_tokenAmount","type":"uint256"},{"internalType":"uint32","name":"_expirationTimestamp","type":"uint32"},{"internalType":"bytes","name":"_collateralizableAllowanceSignature","type":"bytes"},{"internalType":"bytes","name":"_tag","type":"bytes"}],"name":"createStaticLOC","outputs":[{"internalType":"uint96","name":"","type":"uint96"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"eip712Domain","outputs":[{"internalType":"bytes1","name":"fields","type":"bytes1"},{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"version","type":"string"},{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"address","name":"verifyingContract","type":"address"},{"internalType":"bytes32","name":"salt","type":"bytes32"},{"internalType":"uint256[]","name":"extensions","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint96","name":"_locId","type":"uint96"},{"internalType":"uint32","name":"_newExpirationTimestamp","type":"uint32"}],"name":"extendLOC","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_collateralTokenAddress","type":"address"},{"internalType":"address","name":"_creditedTokenAddress","type":"address"}],"name":"getCollateralFactor","outputs":[{"components":[{"internalType":"uint16","name":"creationCollateralFactorBasisPoints","type":"uint16"},{"internalType":"uint16","name":"collateralFactorBasisPoints","type":"uint16"},{"internalType":"uint16","name":"liquidatorIncentiveBasisPoints","type":"uint16"},{"internalType":"uint16","name":"redeemBufferBasisPoints","type":"uint16"}],"internalType":"struct LetterOfCreditStorage.CollateralFactor","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_address","type":"address"}],"name":"getCreditedToken","outputs":[{"components":[{"internalType":"uint256","name":"minPerDynamicLOC","type":"uint256"},{"internalType":"uint256","name":"maxPerDynamicLOC","type":"uint256"},{"internalType":"uint256","name":"globalMaxInDynamicUse","type":"uint256"},{"internalType":"uint256","name":"globalAmountInDynamicUse","type":"uint256"}],"internalType":"struct LetterOfCreditStorage.CreditedToken","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint96","name":"_id","type":"uint96"}],"name":"getLOC","outputs":[{"components":[{"internalType":"uint96","name":"collateralId","type":"uint96"},{"internalType":"address","name":"creator","type":"address"},{"internalType":"address","name":"beneficiary","type":"address"},{"internalType":"uint32","name":"expirationTimestamp","type":"uint32"},{"internalType":"uint16","name":"collateralFactorBasisPoints","type":"uint16"},{"internalType":"uint16","name":"liquidatorIncentiveBasisPoints","type":"uint16"},{"internalType":"contract ICollateral","name":"collateralContract","type":"address"},{"internalType":"address","name":"collateralTokenAddress","type":"address"},{"internalType":"uint256","name":"collateralTokenAmount","type":"uint256"},{"internalType":"uint256","name":"claimableCollateral","type":"uint256"},{"internalType":"address","name":"creditedTokenAddress","type":"address"},{"internalType":"uint256","name":"creditedTokenAmount","type":"uint256"}],"internalType":"struct LetterOfCreditStorage.LOC","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"contract ICollateral","name":"_collateralContract","type":"address"},{"internalType":"contract IPriceOracle","name":"_priceOracle","type":"address"},{"internalType":"uint16","name":"_maxPriceUpdateSecondsAgo","type":"uint16"},{"internalType":"uint32","name":"_maxLocDurationSeconds","type":"uint32"},{"components":[{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"minPerDynamicLOC","type":"uint256"},{"internalType":"uint256","name":"maxPerDynamicLOC","type":"uint256"},{"internalType":"uint256","name":"globalMaxInDynamicUse","type":"uint256"}],"internalType":"struct LetterOfCredit.CreditedTokenConfig[]","name":"_creditedTokens","type":"tuple[]"},{"components":[{"internalType":"address","name":"collateralTokenAddress","type":"address"},{"internalType":"address","name":"creditedTokenAddress","type":"address"},{"components":[{"internalType":"uint16","name":"creationCollateralFactorBasisPoints","type":"uint16"},{"internalType":"uint16","name":"collateralFactorBasisPoints","type":"uint16"},{"internalType":"uint16","name":"liquidatorIncentiveBasisPoints","type":"uint16"},{"internalType":"uint16","name":"redeemBufferBasisPoints","type":"uint16"}],"internalType":"struct LetterOfCreditStorage.CollateralFactor","name":"collateralFactor","type":"tuple"}],"internalType":"struct LetterOfCredit.AssetPairCollateralFactor[]","name":"_assetPairCollateralFactors","type":"tuple[]"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"maxLocDurationSeconds","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxPriceUpdateSecondsAgo","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint96","name":"_locId","type":"uint96"},{"internalType":"int256","name":"_byAmount","type":"int256"},{"internalType":"bytes","name":"_oraclePriceUpdate","type":"bytes"},{"internalType":"bytes","name":"_collateralizableAllowanceSignature","type":"bytes"}],"name":"modifyLOCCollateral","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"bytes32","name":"_signatureType","type":"bytes32"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"priceOracle","outputs":[{"internalType":"contract IPriceOracle","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint96","name":"_locId","type":"uint96"},{"internalType":"uint256","name":"_creditedAmountToRedeem","type":"uint256"},{"internalType":"address","name":"_destinationAddress","type":"address"},{"internalType":"address","name":"_iLiquidatorToUse","type":"address"},{"internalType":"bytes","name":"_oraclePriceUpdate","type":"bytes"},{"internalType":"bytes","name":"_beneficiaryAuthorization","type":"bytes"},{"internalType":"bytes","name":"_liquidatorParams","type":"bytes"}],"name":"redeemLOC","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"_newMaxLocDurationSeconds","type":"uint32"}],"name":"updateMaxLocDurationSeconds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"_maxPriceUpdateSecondsAgo","type":"uint16"}],"name":"updateMaxPriceUpdateSecondsAgo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract ICollateral","name":"_collateralContract","type":"address"}],"name":"upgradeCollateralContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IPriceOracle","name":"_newPriceOracle","type":"address"}],"name":"upgradePriceOracle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"collateralTokenAddress","type":"address"},{"internalType":"address","name":"creditedTokenAddress","type":"address"},{"components":[{"internalType":"uint16","name":"creationCollateralFactorBasisPoints","type":"uint16"},{"internalType":"uint16","name":"collateralFactorBasisPoints","type":"uint16"},{"internalType":"uint16","name":"liquidatorIncentiveBasisPoints","type":"uint16"},{"internalType":"uint16","name":"redeemBufferBasisPoints","type":"uint16"}],"internalType":"struct LetterOfCreditStorage.CollateralFactor","name":"collateralFactor","type":"tuple"}],"internalType":"struct LetterOfCredit.AssetPairCollateralFactor[]","name":"_assetPairCollateralFactors","type":"tuple[]"}],"name":"upsertCollateralFactors","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"minPerDynamicLOC","type":"uint256"},{"internalType":"uint256","name":"maxPerDynamicLOC","type":"uint256"},{"internalType":"uint256","name":"globalMaxInDynamicUse","type":"uint256"}],"internalType":"struct LetterOfCredit.CreditedTokenConfig[]","name":"_creditedTokens","type":"tuple[]"}],"name":"upsertCreditedTokens","outputs":[],"stateMutability":"nonpayable","type":"function"}],"is_changed_bytecode":false,"is_partially_verified":false,"constructor_args":null}