ETH Price: $3,887.68 (+1.81%)
Gas: 0.25 Gwei

Transaction Decoder

Block:
22658203 at Jun-08-2025 06:52:59 AM +UTC
Transaction Fee:
0.000385181041739772 ETH $1.50
Gas Used:
293,099 Gas / 1.314167028 Gwei

Account State Difference:

  Address   Before After State Difference Code
0x4791Eb22...d5a805fF6
1.506649659724111122 Eth
Nonce: 3068
1.50626447868237135 Eth
Nonce: 3069
0.000385181041739772
(Titan Builder)
11.08948157192859333 Eth11.089732308408620074 Eth0.000250736480026744
0xe7Ae9688...58D97ACaa

Execution Trace

0xe445fbe2b1659e078405d1289ac9a13fb8d4d421.60806040( )
  • DelNorteClubToken.permit( owner=0xd59453b76C9e1f50B1af177356793c4608729528, spender=0xe445fbe2B1659E078405D1289Ac9a13FB8d4D421, value=15513000000000000, deadline=1749369169, v=27, r=17A79584303461831592259409F01FE2AB489839A827847F6C24A57A9A1A8740, s=7B9631BDF7CBDAEF0D9E11668068AA0C3B4EFB26A84D58C874AB8584B781A1C8 )
    • Null: 0x000...001.5174635c( )
    • DelNorteClubToken.transferFrom( sender=0xd59453b76C9e1f50B1af177356793c4608729528, recipient=0x4791Eb2224D272655e8d5da171bB07Dd5a805fF6, amount=15513000000000000 ) => ( True )
    • 0x4791eb2224d272655e8d5da171bb07dd5a805ff6.SELFDESTRUCT( )
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC5267.sol)
      pragma solidity ^0.8.0;
      interface IERC5267 {
          /**
           * @dev MAY be emitted to signal that the domain could have changed.
           */
          event EIP712DomainChanged();
          /**
           * @dev returns the fields and values that describe the domain separator used by this contract for EIP-712
           * signature.
           */
          function eip712Domain()
              external
              view
              returns (
                  bytes1 fields,
                  string memory name,
                  string memory version,
                  uint256 chainId,
                  address verifyingContract,
                  bytes32 salt,
                  uint256[] memory extensions
              );
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/ERC20.sol)
      pragma solidity ^0.8.0;
      import "./IERC20.sol";
      import "./extensions/IERC20Metadata.sol";
      import "../../utils/Context.sol";
      /**
       * @dev Implementation of the {IERC20} interface.
       *
       * This implementation is agnostic to the way tokens are created. This means
       * that a supply mechanism has to be added in a derived contract using {_mint}.
       * For a generic mechanism see {ERC20PresetMinterPauser}.
       *
       * TIP: For a detailed writeup see our guide
       * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
       * to implement supply mechanisms].
       *
       * The default value of {decimals} is 18. To change this, you should override
       * this function so it returns a different value.
       *
       * We have followed general OpenZeppelin Contracts guidelines: functions revert
       * instead returning `false` on failure. This behavior is nonetheless
       * conventional and does not conflict with the expectations of ERC20
       * applications.
       *
       * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
       * This allows applications to reconstruct the allowance for all accounts just
       * by listening to said events. Other implementations of the EIP may not emit
       * these events, as it isn't required by the specification.
       *
       * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
       * functions have been added to mitigate the well-known issues around setting
       * allowances. See {IERC20-approve}.
       */
      contract ERC20 is Context, IERC20, IERC20Metadata {
          mapping(address => uint256) private _balances;
          mapping(address => mapping(address => uint256)) private _allowances;
          uint256 private _totalSupply;
          string private _name;
          string private _symbol;
          /**
           * @dev Sets the values for {name} and {symbol}.
           *
           * All two of these values are immutable: they can only be set once during
           * construction.
           */
          constructor(string memory name_, string memory symbol_) {
              _name = name_;
              _symbol = symbol_;
          }
          /**
           * @dev Returns the name of the token.
           */
          function name() public view virtual override returns (string memory) {
              return _name;
          }
          /**
           * @dev Returns the symbol of the token, usually a shorter version of the
           * name.
           */
          function symbol() public view virtual override returns (string memory) {
              return _symbol;
          }
          /**
           * @dev Returns the number of decimals used to get its user representation.
           * For example, if `decimals` equals `2`, a balance of `505` tokens should
           * be displayed to a user as `5.05` (`505 / 10 ** 2`).
           *
           * Tokens usually opt for a value of 18, imitating the relationship between
           * Ether and Wei. This is the default value returned by this function, unless
           * it's overridden.
           *
           * NOTE: This information is only used for _display_ purposes: it in
           * no way affects any of the arithmetic of the contract, including
           * {IERC20-balanceOf} and {IERC20-transfer}.
           */
          function decimals() public view virtual override returns (uint8) {
              return 18;
          }
          /**
           * @dev See {IERC20-totalSupply}.
           */
          function totalSupply() public view virtual override returns (uint256) {
              return _totalSupply;
          }
          /**
           * @dev See {IERC20-balanceOf}.
           */
          function balanceOf(address account) public view virtual override returns (uint256) {
              return _balances[account];
          }
          /**
           * @dev See {IERC20-transfer}.
           *
           * Requirements:
           *
           * - `to` cannot be the zero address.
           * - the caller must have a balance of at least `amount`.
           */
          function transfer(address to, uint256 amount) public virtual override returns (bool) {
              address owner = _msgSender();
              _transfer(owner, to, amount);
              return true;
          }
          /**
           * @dev See {IERC20-allowance}.
           */
          function allowance(address owner, address spender) public view virtual override returns (uint256) {
              return _allowances[owner][spender];
          }
          /**
           * @dev See {IERC20-approve}.
           *
           * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
           * `transferFrom`. This is semantically equivalent to an infinite approval.
           *
           * Requirements:
           *
           * - `spender` cannot be the zero address.
           */
          function approve(address spender, uint256 amount) public virtual override returns (bool) {
              address owner = _msgSender();
              _approve(owner, spender, amount);
              return true;
          }
          /**
           * @dev See {IERC20-transferFrom}.
           *
           * Emits an {Approval} event indicating the updated allowance. This is not
           * required by the EIP. See the note at the beginning of {ERC20}.
           *
           * NOTE: Does not update the allowance if the current allowance
           * is the maximum `uint256`.
           *
           * Requirements:
           *
           * - `from` and `to` cannot be the zero address.
           * - `from` must have a balance of at least `amount`.
           * - the caller must have allowance for ``from``'s tokens of at least
           * `amount`.
           */
          function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) {
              address spender = _msgSender();
              _spendAllowance(from, spender, amount);
              _transfer(from, to, amount);
              return true;
          }
          /**
           * @dev Atomically increases the allowance granted to `spender` by the caller.
           *
           * This is an alternative to {approve} that can be used as a mitigation for
           * problems described in {IERC20-approve}.
           *
           * Emits an {Approval} event indicating the updated allowance.
           *
           * Requirements:
           *
           * - `spender` cannot be the zero address.
           */
          function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
              address owner = _msgSender();
              _approve(owner, spender, allowance(owner, spender) + addedValue);
              return true;
          }
          /**
           * @dev Atomically decreases the allowance granted to `spender` by the caller.
           *
           * This is an alternative to {approve} that can be used as a mitigation for
           * problems described in {IERC20-approve}.
           *
           * Emits an {Approval} event indicating the updated allowance.
           *
           * Requirements:
           *
           * - `spender` cannot be the zero address.
           * - `spender` must have allowance for the caller of at least
           * `subtractedValue`.
           */
          function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
              address owner = _msgSender();
              uint256 currentAllowance = allowance(owner, spender);
              require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
              unchecked {
                  _approve(owner, spender, currentAllowance - subtractedValue);
              }
              return true;
          }
          /**
           * @dev Moves `amount` of tokens from `from` to `to`.
           *
           * This internal function is equivalent to {transfer}, and can be used to
           * e.g. implement automatic token fees, slashing mechanisms, etc.
           *
           * Emits a {Transfer} event.
           *
           * Requirements:
           *
           * - `from` cannot be the zero address.
           * - `to` cannot be the zero address.
           * - `from` must have a balance of at least `amount`.
           */
          function _transfer(address from, address to, uint256 amount) internal virtual {
              require(from != address(0), "ERC20: transfer from the zero address");
              require(to != address(0), "ERC20: transfer to the zero address");
              _beforeTokenTransfer(from, to, amount);
              uint256 fromBalance = _balances[from];
              require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
              unchecked {
                  _balances[from] = fromBalance - amount;
                  // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by
                  // decrementing then incrementing.
                  _balances[to] += amount;
              }
              emit Transfer(from, to, amount);
              _afterTokenTransfer(from, to, amount);
          }
          /** @dev Creates `amount` tokens and assigns them to `account`, increasing
           * the total supply.
           *
           * Emits a {Transfer} event with `from` set to the zero address.
           *
           * Requirements:
           *
           * - `account` cannot be the zero address.
           */
          function _mint(address account, uint256 amount) internal virtual {
              require(account != address(0), "ERC20: mint to the zero address");
              _beforeTokenTransfer(address(0), account, amount);
              _totalSupply += amount;
              unchecked {
                  // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.
                  _balances[account] += amount;
              }
              emit Transfer(address(0), account, amount);
              _afterTokenTransfer(address(0), account, amount);
          }
          /**
           * @dev Destroys `amount` tokens from `account`, reducing the
           * total supply.
           *
           * Emits a {Transfer} event with `to` set to the zero address.
           *
           * Requirements:
           *
           * - `account` cannot be the zero address.
           * - `account` must have at least `amount` tokens.
           */
          function _burn(address account, uint256 amount) internal virtual {
              require(account != address(0), "ERC20: burn from the zero address");
              _beforeTokenTransfer(account, address(0), amount);
              uint256 accountBalance = _balances[account];
              require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
              unchecked {
                  _balances[account] = accountBalance - amount;
                  // Overflow not possible: amount <= accountBalance <= totalSupply.
                  _totalSupply -= amount;
              }
              emit Transfer(account, address(0), amount);
              _afterTokenTransfer(account, address(0), amount);
          }
          /**
           * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
           *
           * This internal function is equivalent to `approve`, and can be used to
           * e.g. set automatic allowances for certain subsystems, etc.
           *
           * Emits an {Approval} event.
           *
           * Requirements:
           *
           * - `owner` cannot be the zero address.
           * - `spender` cannot be the zero address.
           */
          function _approve(address owner, address spender, uint256 amount) internal virtual {
              require(owner != address(0), "ERC20: approve from the zero address");
              require(spender != address(0), "ERC20: approve to the zero address");
              _allowances[owner][spender] = amount;
              emit Approval(owner, spender, amount);
          }
          /**
           * @dev Updates `owner` s allowance for `spender` based on spent `amount`.
           *
           * Does not update the allowance amount in case of infinite allowance.
           * Revert if not enough allowance is available.
           *
           * Might emit an {Approval} event.
           */
          function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {
              uint256 currentAllowance = allowance(owner, spender);
              if (currentAllowance != type(uint256).max) {
                  require(currentAllowance >= amount, "ERC20: insufficient allowance");
                  unchecked {
                      _approve(owner, spender, currentAllowance - amount);
                  }
              }
          }
          /**
           * @dev Hook that is called before any transfer of tokens. This includes
           * minting and burning.
           *
           * Calling conditions:
           *
           * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
           * will be transferred to `to`.
           * - when `from` is zero, `amount` tokens will be minted for `to`.
           * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
           * - `from` and `to` are never both zero.
           *
           * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
           */
          function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {}
          /**
           * @dev Hook that is called after any transfer of tokens. This includes
           * minting and burning.
           *
           * Calling conditions:
           *
           * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
           * has been transferred to `to`.
           * - when `from` is zero, `amount` tokens have been minted for `to`.
           * - when `to` is zero, `amount` of ``from``'s tokens have been burned.
           * - `from` and `to` are never both zero.
           *
           * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
           */
          function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {}
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/extensions/ERC20Burnable.sol)
      pragma solidity ^0.8.0;
      import "../ERC20.sol";
      import "../../../utils/Context.sol";
      /**
       * @dev Extension of {ERC20} that allows token holders to destroy both their own
       * tokens and those that they have an allowance for, in a way that can be
       * recognized off-chain (via event analysis).
       */
      abstract contract ERC20Burnable is Context, ERC20 {
          /**
           * @dev Destroys `amount` tokens from the caller.
           *
           * See {ERC20-_burn}.
           */
          function burn(uint256 amount) public virtual {
              _burn(_msgSender(), amount);
          }
          /**
           * @dev Destroys `amount` tokens from `account`, deducting from the caller's
           * allowance.
           *
           * See {ERC20-_burn} and {ERC20-allowance}.
           *
           * Requirements:
           *
           * - the caller must have allowance for ``accounts``'s tokens of at least
           * `amount`.
           */
          function burnFrom(address account, uint256 amount) public virtual {
              _spendAllowance(account, _msgSender(), amount);
              _burn(account, amount);
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.9.4) (token/ERC20/extensions/ERC20Permit.sol)
      pragma solidity ^0.8.0;
      import "./IERC20Permit.sol";
      import "../ERC20.sol";
      import "../../../utils/cryptography/ECDSA.sol";
      import "../../../utils/cryptography/EIP712.sol";
      import "../../../utils/Counters.sol";
      /**
       * @dev Implementation of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
       * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
       *
       * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
       * presenting a message signed by the account. By not relying on `{IERC20-approve}`, the token holder account doesn't
       * need to send a transaction, and thus is not required to hold Ether at all.
       *
       * _Available since v3.4._
       */
      abstract contract ERC20Permit is ERC20, IERC20Permit, EIP712 {
          using Counters for Counters.Counter;
          mapping(address => Counters.Counter) private _nonces;
          // solhint-disable-next-line var-name-mixedcase
          bytes32 private constant _PERMIT_TYPEHASH =
              keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
          /**
           * @dev In previous versions `_PERMIT_TYPEHASH` was declared as `immutable`.
           * However, to ensure consistency with the upgradeable transpiler, we will continue
           * to reserve a slot.
           * @custom:oz-renamed-from _PERMIT_TYPEHASH
           */
          // solhint-disable-next-line var-name-mixedcase
          bytes32 private _PERMIT_TYPEHASH_DEPRECATED_SLOT;
          /**
           * @dev Initializes the {EIP712} domain separator using the `name` parameter, and setting `version` to `"1"`.
           *
           * It's a good idea to use the same `name` that is defined as the ERC20 token name.
           */
          constructor(string memory name) EIP712(name, "1") {}
          /**
           * @inheritdoc IERC20Permit
           */
          function permit(
              address owner,
              address spender,
              uint256 value,
              uint256 deadline,
              uint8 v,
              bytes32 r,
              bytes32 s
          ) public virtual override {
              require(block.timestamp <= deadline, "ERC20Permit: expired deadline");
              bytes32 structHash = keccak256(abi.encode(_PERMIT_TYPEHASH, owner, spender, value, _useNonce(owner), deadline));
              bytes32 hash = _hashTypedDataV4(structHash);
              address signer = ECDSA.recover(hash, v, r, s);
              require(signer == owner, "ERC20Permit: invalid signature");
              _approve(owner, spender, value);
          }
          /**
           * @inheritdoc IERC20Permit
           */
          function nonces(address owner) public view virtual override returns (uint256) {
              return _nonces[owner].current();
          }
          /**
           * @inheritdoc IERC20Permit
           */
          // solhint-disable-next-line func-name-mixedcase
          function DOMAIN_SEPARATOR() external view override returns (bytes32) {
              return _domainSeparatorV4();
          }
          /**
           * @dev "Consume a nonce": return the current value and increment.
           *
           * _Available since v4.1._
           */
          function _useNonce(address owner) internal virtual returns (uint256 current) {
              Counters.Counter storage nonce = _nonces[owner];
              current = nonce.current();
              nonce.increment();
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)
      pragma solidity ^0.8.0;
      import "../IERC20.sol";
      /**
       * @dev Interface for the optional metadata functions from the ERC20 standard.
       *
       * _Available since v4.1._
       */
      interface IERC20Metadata is IERC20 {
          /**
           * @dev Returns the name of the token.
           */
          function name() external view returns (string memory);
          /**
           * @dev Returns the symbol of the token.
           */
          function symbol() external view returns (string memory);
          /**
           * @dev Returns the decimals places of the token.
           */
          function decimals() external view returns (uint8);
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.9.4) (token/ERC20/extensions/IERC20Permit.sol)
      pragma solidity ^0.8.0;
      /**
       * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
       * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
       *
       * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
       * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
       * need to send a transaction, and thus is not required to hold Ether at all.
       *
       * ==== Security Considerations
       *
       * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
       * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
       * considered as an intention to spend the allowance in any specific way. The second is that because permits have
       * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
       * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
       * generally recommended is:
       *
       * ```solidity
       * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
       *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
       *     doThing(..., value);
       * }
       *
       * function doThing(..., uint256 value) public {
       *     token.safeTransferFrom(msg.sender, address(this), value);
       *     ...
       * }
       * ```
       *
       * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
       * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
       * {SafeERC20-safeTransferFrom}).
       *
       * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
       * contracts should have entry points that don't rely on permit.
       */
      interface IERC20Permit {
          /**
           * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
           * given ``owner``'s signed approval.
           *
           * IMPORTANT: The same issues {IERC20-approve} has related to transaction
           * ordering also apply here.
           *
           * Emits an {Approval} event.
           *
           * Requirements:
           *
           * - `spender` cannot be the zero address.
           * - `deadline` must be a timestamp in the future.
           * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
           * over the EIP712-formatted function arguments.
           * - the signature must use ``owner``'s current nonce (see {nonces}).
           *
           * For more information on the signature format, see the
           * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
           * section].
           *
           * CAUTION: See Security Considerations above.
           */
          function permit(
              address owner,
              address spender,
              uint256 value,
              uint256 deadline,
              uint8 v,
              bytes32 r,
              bytes32 s
          ) external;
          /**
           * @dev Returns the current nonce for `owner`. This value must be
           * included whenever a signature is generated for {permit}.
           *
           * Every successful call to {permit} increases ``owner``'s nonce by one. This
           * prevents a signature from being used multiple times.
           */
          function nonces(address owner) external view returns (uint256);
          /**
           * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
           */
          // solhint-disable-next-line func-name-mixedcase
          function DOMAIN_SEPARATOR() external view returns (bytes32);
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
      pragma solidity ^0.8.0;
      /**
       * @dev Interface of the ERC20 standard as defined in the EIP.
       */
      interface IERC20 {
          /**
           * @dev Emitted when `value` tokens are moved from one account (`from`) to
           * another (`to`).
           *
           * Note that `value` may be zero.
           */
          event Transfer(address indexed from, address indexed to, uint256 value);
          /**
           * @dev Emitted when the allowance of a `spender` for an `owner` is set by
           * a call to {approve}. `value` is the new allowance.
           */
          event Approval(address indexed owner, address indexed spender, uint256 value);
          /**
           * @dev Returns the amount of tokens in existence.
           */
          function totalSupply() external view returns (uint256);
          /**
           * @dev Returns the amount of tokens owned by `account`.
           */
          function balanceOf(address account) external view returns (uint256);
          /**
           * @dev Moves `amount` tokens from the caller's account to `to`.
           *
           * Returns a boolean value indicating whether the operation succeeded.
           *
           * Emits a {Transfer} event.
           */
          function transfer(address to, uint256 amount) external returns (bool);
          /**
           * @dev Returns the remaining number of tokens that `spender` will be
           * allowed to spend on behalf of `owner` through {transferFrom}. This is
           * zero by default.
           *
           * This value changes when {approve} or {transferFrom} are called.
           */
          function allowance(address owner, address spender) external view returns (uint256);
          /**
           * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
           *
           * Returns a boolean value indicating whether the operation succeeded.
           *
           * IMPORTANT: Beware that changing an allowance with this method brings the risk
           * that someone may use both the old and the new allowance by unfortunate
           * transaction ordering. One possible solution to mitigate this race
           * condition is to first reduce the spender's allowance to 0 and set the
           * desired value afterwards:
           * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
           *
           * Emits an {Approval} event.
           */
          function approve(address spender, uint256 amount) external returns (bool);
          /**
           * @dev Moves `amount` tokens from `from` to `to` using the
           * allowance mechanism. `amount` is then deducted from the caller's
           * allowance.
           *
           * Returns a boolean value indicating whether the operation succeeded.
           *
           * Emits a {Transfer} event.
           */
          function transferFrom(address from, address to, uint256 amount) external returns (bool);
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol)
      pragma solidity ^0.8.0;
      import "../IERC20.sol";
      import "../extensions/IERC20Permit.sol";
      import "../../../utils/Address.sol";
      /**
       * @title SafeERC20
       * @dev Wrappers around ERC20 operations that throw on failure (when the token
       * contract returns false). Tokens that return no value (and instead revert or
       * throw on failure) are also supported, non-reverting calls are assumed to be
       * successful.
       * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
       * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
       */
      library SafeERC20 {
          using Address for address;
          /**
           * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
           * non-reverting calls are assumed to be successful.
           */
          function safeTransfer(IERC20 token, address to, uint256 value) internal {
              _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
          }
          /**
           * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
           * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
           */
          function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
              _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
          }
          /**
           * @dev Deprecated. This function has issues similar to the ones found in
           * {IERC20-approve}, and its usage is discouraged.
           *
           * Whenever possible, use {safeIncreaseAllowance} and
           * {safeDecreaseAllowance} instead.
           */
          function safeApprove(IERC20 token, address spender, uint256 value) internal {
              // safeApprove should only be called when setting an initial allowance,
              // or when resetting it to zero. To increase and decrease it, use
              // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
              require(
                  (value == 0) || (token.allowance(address(this), spender) == 0),
                  "SafeERC20: approve from non-zero to non-zero allowance"
              );
              _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
          }
          /**
           * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
           * non-reverting calls are assumed to be successful.
           */
          function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
              uint256 oldAllowance = token.allowance(address(this), spender);
              _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
          }
          /**
           * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
           * non-reverting calls are assumed to be successful.
           */
          function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
              unchecked {
                  uint256 oldAllowance = token.allowance(address(this), spender);
                  require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
                  _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
              }
          }
          /**
           * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
           * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
           * to be set to zero before setting it to a non-zero value, such as USDT.
           */
          function forceApprove(IERC20 token, address spender, uint256 value) internal {
              bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);
              if (!_callOptionalReturnBool(token, approvalCall)) {
                  _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
                  _callOptionalReturn(token, approvalCall);
              }
          }
          /**
           * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
           * Revert on invalid signature.
           */
          function safePermit(
              IERC20Permit token,
              address owner,
              address spender,
              uint256 value,
              uint256 deadline,
              uint8 v,
              bytes32 r,
              bytes32 s
          ) internal {
              uint256 nonceBefore = token.nonces(owner);
              token.permit(owner, spender, value, deadline, v, r, s);
              uint256 nonceAfter = token.nonces(owner);
              require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
          }
          /**
           * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
           * on the return value: the return value is optional (but if data is returned, it must not be false).
           * @param token The token targeted by the call.
           * @param data The call data (encoded using abi.encode or one of its variants).
           */
          function _callOptionalReturn(IERC20 token, bytes memory data) private {
              // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
              // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
              // the target address contains contract code and also asserts for success in the low-level call.
              bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
              require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
          }
          /**
           * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
           * on the return value: the return value is optional (but if data is returned, it must not be false).
           * @param token The token targeted by the call.
           * @param data The call data (encoded using abi.encode or one of its variants).
           *
           * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
           */
          function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
              // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
              // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
              // and not revert is the subcall reverts.
              (bool success, bytes memory returndata) = address(token).call(data);
              return
                  success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
      pragma solidity ^0.8.1;
      /**
       * @dev Collection of functions related to the address type
       */
      library Address {
          /**
           * @dev Returns true if `account` is a contract.
           *
           * [IMPORTANT]
           * ====
           * It is unsafe to assume that an address for which this function returns
           * false is an externally-owned account (EOA) and not a contract.
           *
           * Among others, `isContract` will return false for the following
           * types of addresses:
           *
           *  - an externally-owned account
           *  - a contract in construction
           *  - an address where a contract will be created
           *  - an address where a contract lived, but was destroyed
           *
           * Furthermore, `isContract` will also return true if the target contract within
           * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
           * which only has an effect at the end of a transaction.
           * ====
           *
           * [IMPORTANT]
           * ====
           * You shouldn't rely on `isContract` to protect against flash loan attacks!
           *
           * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
           * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
           * constructor.
           * ====
           */
          function isContract(address account) internal view returns (bool) {
              // This method relies on extcodesize/address.code.length, which returns 0
              // for contracts in construction, since the code is only stored at the end
              // of the constructor execution.
              return account.code.length > 0;
          }
          /**
           * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
           * `recipient`, forwarding all available gas and reverting on errors.
           *
           * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
           * of certain opcodes, possibly making contracts go over the 2300 gas limit
           * imposed by `transfer`, making them unable to receive funds via
           * `transfer`. {sendValue} removes this limitation.
           *
           * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
           *
           * IMPORTANT: because control is transferred to `recipient`, care must be
           * taken to not create reentrancy vulnerabilities. Consider using
           * {ReentrancyGuard} or the
           * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
           */
          function sendValue(address payable recipient, uint256 amount) internal {
              require(address(this).balance >= amount, "Address: insufficient balance");
              (bool success, ) = recipient.call{value: amount}("");
              require(success, "Address: unable to send value, recipient may have reverted");
          }
          /**
           * @dev Performs a Solidity function call using a low level `call`. A
           * plain `call` is an unsafe replacement for a function call: use this
           * function instead.
           *
           * If `target` reverts with a revert reason, it is bubbled up by this
           * function (like regular Solidity function calls).
           *
           * Returns the raw returned data. To convert to the expected return value,
           * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
           *
           * Requirements:
           *
           * - `target` must be a contract.
           * - calling `target` with `data` must not revert.
           *
           * _Available since v3.1._
           */
          function functionCall(address target, bytes memory data) internal returns (bytes memory) {
              return functionCallWithValue(target, data, 0, "Address: low-level call failed");
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
           * `errorMessage` as a fallback revert reason when `target` reverts.
           *
           * _Available since v3.1._
           */
          function functionCall(
              address target,
              bytes memory data,
              string memory errorMessage
          ) internal returns (bytes memory) {
              return functionCallWithValue(target, data, 0, errorMessage);
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but also transferring `value` wei to `target`.
           *
           * Requirements:
           *
           * - the calling contract must have an ETH balance of at least `value`.
           * - the called Solidity function must be `payable`.
           *
           * _Available since v3.1._
           */
          function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
              return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
          }
          /**
           * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
           * with `errorMessage` as a fallback revert reason when `target` reverts.
           *
           * _Available since v3.1._
           */
          function functionCallWithValue(
              address target,
              bytes memory data,
              uint256 value,
              string memory errorMessage
          ) internal returns (bytes memory) {
              require(address(this).balance >= value, "Address: insufficient balance for call");
              (bool success, bytes memory returndata) = target.call{value: value}(data);
              return verifyCallResultFromTarget(target, success, returndata, errorMessage);
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but performing a static call.
           *
           * _Available since v3.3._
           */
          function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
              return functionStaticCall(target, data, "Address: low-level static call failed");
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
           * but performing a static call.
           *
           * _Available since v3.3._
           */
          function functionStaticCall(
              address target,
              bytes memory data,
              string memory errorMessage
          ) internal view returns (bytes memory) {
              (bool success, bytes memory returndata) = target.staticcall(data);
              return verifyCallResultFromTarget(target, success, returndata, errorMessage);
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but performing a delegate call.
           *
           * _Available since v3.4._
           */
          function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
              return functionDelegateCall(target, data, "Address: low-level delegate call failed");
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
           * but performing a delegate call.
           *
           * _Available since v3.4._
           */
          function functionDelegateCall(
              address target,
              bytes memory data,
              string memory errorMessage
          ) internal returns (bytes memory) {
              (bool success, bytes memory returndata) = target.delegatecall(data);
              return verifyCallResultFromTarget(target, success, returndata, errorMessage);
          }
          /**
           * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
           * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
           *
           * _Available since v4.8._
           */
          function verifyCallResultFromTarget(
              address target,
              bool success,
              bytes memory returndata,
              string memory errorMessage
          ) internal view returns (bytes memory) {
              if (success) {
                  if (returndata.length == 0) {
                      // only check isContract if the call was successful and the return data is empty
                      // otherwise we already know that it was a contract
                      require(isContract(target), "Address: call to non-contract");
                  }
                  return returndata;
              } else {
                  _revert(returndata, errorMessage);
              }
          }
          /**
           * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
           * revert reason or using the provided one.
           *
           * _Available since v4.3._
           */
          function verifyCallResult(
              bool success,
              bytes memory returndata,
              string memory errorMessage
          ) internal pure returns (bytes memory) {
              if (success) {
                  return returndata;
              } else {
                  _revert(returndata, errorMessage);
              }
          }
          function _revert(bytes memory returndata, string memory errorMessage) private pure {
              // Look for revert reason and bubble it up if present
              if (returndata.length > 0) {
                  // The easiest way to bubble the revert reason is using memory via assembly
                  /// @solidity memory-safe-assembly
                  assembly {
                      let returndata_size := mload(returndata)
                      revert(add(32, returndata), returndata_size)
                  }
              } else {
                  revert(errorMessage);
              }
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.9.4) (utils/Context.sol)
      pragma solidity ^0.8.0;
      /**
       * @dev Provides information about the current execution context, including the
       * sender of the transaction and its data. While these are generally available
       * via msg.sender and msg.data, they should not be accessed in such a direct
       * manner, since when dealing with meta-transactions the account sending and
       * paying for execution may not be the actual sender (as far as an application
       * is concerned).
       *
       * This contract is only required for intermediate, library-like contracts.
       */
      abstract contract Context {
          function _msgSender() internal view virtual returns (address) {
              return msg.sender;
          }
          function _msgData() internal view virtual returns (bytes calldata) {
              return msg.data;
          }
          function _contextSuffixLength() internal view virtual returns (uint256) {
              return 0;
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (utils/Counters.sol)
      pragma solidity ^0.8.0;
      /**
       * @title Counters
       * @author Matt Condon (@shrugs)
       * @dev Provides counters that can only be incremented, decremented or reset. This can be used e.g. to track the number
       * of elements in a mapping, issuing ERC721 ids, or counting request ids.
       *
       * Include with `using Counters for Counters.Counter;`
       */
      library Counters {
          struct Counter {
              // This variable should never be directly accessed by users of the library: interactions must be restricted to
              // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add
              // this feature: see https://github.com/ethereum/solidity/issues/4637
              uint256 _value; // default: 0
          }
          function current(Counter storage counter) internal view returns (uint256) {
              return counter._value;
          }
          function increment(Counter storage counter) internal {
              unchecked {
                  counter._value += 1;
              }
          }
          function decrement(Counter storage counter) internal {
              uint256 value = counter._value;
              require(value > 0, "Counter: decrement overflow");
              unchecked {
                  counter._value = value - 1;
              }
          }
          function reset(Counter storage counter) internal {
              counter._value = 0;
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/ECDSA.sol)
      pragma solidity ^0.8.0;
      import "../Strings.sol";
      /**
       * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
       *
       * These functions can be used to verify that a message was signed by the holder
       * of the private keys of a given address.
       */
      library ECDSA {
          enum RecoverError {
              NoError,
              InvalidSignature,
              InvalidSignatureLength,
              InvalidSignatureS,
              InvalidSignatureV // Deprecated in v4.8
          }
          function _throwError(RecoverError error) private pure {
              if (error == RecoverError.NoError) {
                  return; // no error: do nothing
              } else if (error == RecoverError.InvalidSignature) {
                  revert("ECDSA: invalid signature");
              } else if (error == RecoverError.InvalidSignatureLength) {
                  revert("ECDSA: invalid signature length");
              } else if (error == RecoverError.InvalidSignatureS) {
                  revert("ECDSA: invalid signature 's' value");
              }
          }
          /**
           * @dev Returns the address that signed a hashed message (`hash`) with
           * `signature` or error string. This address can then be used for verification purposes.
           *
           * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
           * this function rejects them by requiring the `s` value to be in the lower
           * half order, and the `v` value to be either 27 or 28.
           *
           * IMPORTANT: `hash` _must_ be the result of a hash operation for the
           * verification to be secure: it is possible to craft signatures that
           * recover to arbitrary addresses for non-hashed data. A safe way to ensure
           * this is by receiving a hash of the original message (which may otherwise
           * be too long), and then calling {toEthSignedMessageHash} on it.
           *
           * Documentation for signature generation:
           * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
           * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
           *
           * _Available since v4.3._
           */
          function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
              if (signature.length == 65) {
                  bytes32 r;
                  bytes32 s;
                  uint8 v;
                  // ecrecover takes the signature parameters, and the only way to get them
                  // currently is to use assembly.
                  /// @solidity memory-safe-assembly
                  assembly {
                      r := mload(add(signature, 0x20))
                      s := mload(add(signature, 0x40))
                      v := byte(0, mload(add(signature, 0x60)))
                  }
                  return tryRecover(hash, v, r, s);
              } else {
                  return (address(0), RecoverError.InvalidSignatureLength);
              }
          }
          /**
           * @dev Returns the address that signed a hashed message (`hash`) with
           * `signature`. This address can then be used for verification purposes.
           *
           * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
           * this function rejects them by requiring the `s` value to be in the lower
           * half order, and the `v` value to be either 27 or 28.
           *
           * IMPORTANT: `hash` _must_ be the result of a hash operation for the
           * verification to be secure: it is possible to craft signatures that
           * recover to arbitrary addresses for non-hashed data. A safe way to ensure
           * this is by receiving a hash of the original message (which may otherwise
           * be too long), and then calling {toEthSignedMessageHash} on it.
           */
          function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
              (address recovered, RecoverError error) = tryRecover(hash, signature);
              _throwError(error);
              return recovered;
          }
          /**
           * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
           *
           * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
           *
           * _Available since v4.3._
           */
          function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError) {
              bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
              uint8 v = uint8((uint256(vs) >> 255) + 27);
              return tryRecover(hash, v, r, s);
          }
          /**
           * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
           *
           * _Available since v4.2._
           */
          function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
              (address recovered, RecoverError error) = tryRecover(hash, r, vs);
              _throwError(error);
              return recovered;
          }
          /**
           * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
           * `r` and `s` signature fields separately.
           *
           * _Available since v4.3._
           */
          function tryRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address, RecoverError) {
              // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
              // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
              // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
              // signatures from current libraries generate a unique signature with an s-value in the lower half order.
              //
              // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
              // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
              // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
              // these malleable signatures as well.
              if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
                  return (address(0), RecoverError.InvalidSignatureS);
              }
              // If the signature is valid (and not malleable), return the signer address
              address signer = ecrecover(hash, v, r, s);
              if (signer == address(0)) {
                  return (address(0), RecoverError.InvalidSignature);
              }
              return (signer, RecoverError.NoError);
          }
          /**
           * @dev Overload of {ECDSA-recover} that receives the `v`,
           * `r` and `s` signature fields separately.
           */
          function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
              (address recovered, RecoverError error) = tryRecover(hash, v, r, s);
              _throwError(error);
              return recovered;
          }
          /**
           * @dev Returns an Ethereum Signed Message, created from a `hash`. This
           * produces hash corresponding to the one signed with the
           * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
           * JSON-RPC method as part of EIP-191.
           *
           * See {recover}.
           */
          function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 message) {
              // 32 is the length in bytes of hash,
              // enforced by the type signature above
              /// @solidity memory-safe-assembly
              assembly {
                  mstore(0x00, "\\x19Ethereum Signed Message:\
      32")
                  mstore(0x1c, hash)
                  message := keccak256(0x00, 0x3c)
              }
          }
          /**
           * @dev Returns an Ethereum Signed Message, created from `s`. This
           * produces hash corresponding to the one signed with the
           * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
           * JSON-RPC method as part of EIP-191.
           *
           * See {recover}.
           */
          function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
              return keccak256(abi.encodePacked("\\x19Ethereum Signed Message:\
      ", Strings.toString(s.length), s));
          }
          /**
           * @dev Returns an Ethereum Signed Typed Data, created from a
           * `domainSeparator` and a `structHash`. This produces hash corresponding
           * to the one signed with the
           * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
           * JSON-RPC method as part of EIP-712.
           *
           * See {recover}.
           */
          function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 data) {
              /// @solidity memory-safe-assembly
              assembly {
                  let ptr := mload(0x40)
                  mstore(ptr, "\\x19\\x01")
                  mstore(add(ptr, 0x02), domainSeparator)
                  mstore(add(ptr, 0x22), structHash)
                  data := keccak256(ptr, 0x42)
              }
          }
          /**
           * @dev Returns an Ethereum Signed Data with intended validator, created from a
           * `validator` and `data` according to the version 0 of EIP-191.
           *
           * See {recover}.
           */
          function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
              return keccak256(abi.encodePacked("\\x19\\x00", validator, data));
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/EIP712.sol)
      pragma solidity ^0.8.8;
      import "./ECDSA.sol";
      import "../ShortStrings.sol";
      import "../../interfaces/IERC5267.sol";
      /**
       * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
       *
       * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible,
       * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding
       * they need in their contracts using a combination of `abi.encode` and `keccak256`.
       *
       * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
       * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
       * ({_hashTypedDataV4}).
       *
       * The implementation of the domain separator was designed to be as efficient as possible while still properly updating
       * the chain id to protect against replay attacks on an eventual fork of the chain.
       *
       * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
       * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
       *
       * NOTE: In the upgradeable version of this contract, the cached values will correspond to the address, and the domain
       * separator of the implementation contract. This will cause the `_domainSeparatorV4` function to always rebuild the
       * separator from the immutable values, which is cheaper than accessing a cached version in cold storage.
       *
       * _Available since v3.4._
       *
       * @custom:oz-upgrades-unsafe-allow state-variable-immutable state-variable-assignment
       */
      abstract contract EIP712 is IERC5267 {
          using ShortStrings for *;
          bytes32 private constant _TYPE_HASH =
              keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
          // Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to
          // invalidate the cached domain separator if the chain id changes.
          bytes32 private immutable _cachedDomainSeparator;
          uint256 private immutable _cachedChainId;
          address private immutable _cachedThis;
          bytes32 private immutable _hashedName;
          bytes32 private immutable _hashedVersion;
          ShortString private immutable _name;
          ShortString private immutable _version;
          string private _nameFallback;
          string private _versionFallback;
          /**
           * @dev Initializes the domain separator and parameter caches.
           *
           * The meaning of `name` and `version` is specified in
           * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
           *
           * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
           * - `version`: the current major version of the signing domain.
           *
           * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
           * contract upgrade].
           */
          constructor(string memory name, string memory version) {
              _name = name.toShortStringWithFallback(_nameFallback);
              _version = version.toShortStringWithFallback(_versionFallback);
              _hashedName = keccak256(bytes(name));
              _hashedVersion = keccak256(bytes(version));
              _cachedChainId = block.chainid;
              _cachedDomainSeparator = _buildDomainSeparator();
              _cachedThis = address(this);
          }
          /**
           * @dev Returns the domain separator for the current chain.
           */
          function _domainSeparatorV4() internal view returns (bytes32) {
              if (address(this) == _cachedThis && block.chainid == _cachedChainId) {
                  return _cachedDomainSeparator;
              } else {
                  return _buildDomainSeparator();
              }
          }
          function _buildDomainSeparator() private view returns (bytes32) {
              return keccak256(abi.encode(_TYPE_HASH, _hashedName, _hashedVersion, block.chainid, address(this)));
          }
          /**
           * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
           * function returns the hash of the fully encoded EIP712 message for this domain.
           *
           * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
           *
           * ```solidity
           * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
           *     keccak256("Mail(address to,string contents)"),
           *     mailTo,
           *     keccak256(bytes(mailContents))
           * )));
           * address signer = ECDSA.recover(digest, signature);
           * ```
           */
          function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
              return ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash);
          }
          /**
           * @dev See {EIP-5267}.
           *
           * _Available since v4.9._
           */
          function eip712Domain()
              public
              view
              virtual
              override
              returns (
                  bytes1 fields,
                  string memory name,
                  string memory version,
                  uint256 chainId,
                  address verifyingContract,
                  bytes32 salt,
                  uint256[] memory extensions
              )
          {
              return (
                  hex"0f", // 01111
                  _name.toStringWithFallback(_nameFallback),
                  _version.toStringWithFallback(_versionFallback),
                  block.chainid,
                  address(this),
                  bytes32(0),
                  new uint256[](0)
              );
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)
      pragma solidity ^0.8.0;
      /**
       * @dev Standard math utilities missing in the Solidity language.
       */
      library Math {
          enum Rounding {
              Down, // Toward negative infinity
              Up, // Toward infinity
              Zero // Toward zero
          }
          /**
           * @dev Returns the largest of two numbers.
           */
          function max(uint256 a, uint256 b) internal pure returns (uint256) {
              return a > b ? a : b;
          }
          /**
           * @dev Returns the smallest of two numbers.
           */
          function min(uint256 a, uint256 b) internal pure returns (uint256) {
              return a < b ? a : b;
          }
          /**
           * @dev Returns the average of two numbers. The result is rounded towards
           * zero.
           */
          function average(uint256 a, uint256 b) internal pure returns (uint256) {
              // (a + b) / 2 can overflow.
              return (a & b) + (a ^ b) / 2;
          }
          /**
           * @dev Returns the ceiling of the division of two numbers.
           *
           * This differs from standard division with `/` in that it rounds up instead
           * of rounding down.
           */
          function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
              // (a + b - 1) / b can overflow on addition, so we distribute.
              return a == 0 ? 0 : (a - 1) / b + 1;
          }
          /**
           * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
           * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
           * with further edits by Uniswap Labs also under MIT license.
           */
          function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
              unchecked {
                  // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
                  // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
                  // variables such that product = prod1 * 2^256 + prod0.
                  uint256 prod0; // Least significant 256 bits of the product
                  uint256 prod1; // Most significant 256 bits of the product
                  assembly {
                      let mm := mulmod(x, y, not(0))
                      prod0 := mul(x, y)
                      prod1 := sub(sub(mm, prod0), lt(mm, prod0))
                  }
                  // Handle non-overflow cases, 256 by 256 division.
                  if (prod1 == 0) {
                      // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                      // The surrounding unchecked block does not change this fact.
                      // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                      return prod0 / denominator;
                  }
                  // Make sure the result is less than 2^256. Also prevents denominator == 0.
                  require(denominator > prod1, "Math: mulDiv overflow");
                  ///////////////////////////////////////////////
                  // 512 by 256 division.
                  ///////////////////////////////////////////////
                  // Make division exact by subtracting the remainder from [prod1 prod0].
                  uint256 remainder;
                  assembly {
                      // Compute remainder using mulmod.
                      remainder := mulmod(x, y, denominator)
                      // Subtract 256 bit number from 512 bit number.
                      prod1 := sub(prod1, gt(remainder, prod0))
                      prod0 := sub(prod0, remainder)
                  }
                  // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
                  // See https://cs.stackexchange.com/q/138556/92363.
                  // Does not overflow because the denominator cannot be zero at this stage in the function.
                  uint256 twos = denominator & (~denominator + 1);
                  assembly {
                      // Divide denominator by twos.
                      denominator := div(denominator, twos)
                      // Divide [prod1 prod0] by twos.
                      prod0 := div(prod0, twos)
                      // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                      twos := add(div(sub(0, twos), twos), 1)
                  }
                  // Shift in bits from prod1 into prod0.
                  prod0 |= prod1 * twos;
                  // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
                  // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
                  // four bits. That is, denominator * inv = 1 mod 2^4.
                  uint256 inverse = (3 * denominator) ^ 2;
                  // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
                  // in modular arithmetic, doubling the correct bits in each step.
                  inverse *= 2 - denominator * inverse; // inverse mod 2^8
                  inverse *= 2 - denominator * inverse; // inverse mod 2^16
                  inverse *= 2 - denominator * inverse; // inverse mod 2^32
                  inverse *= 2 - denominator * inverse; // inverse mod 2^64
                  inverse *= 2 - denominator * inverse; // inverse mod 2^128
                  inverse *= 2 - denominator * inverse; // inverse mod 2^256
                  // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
                  // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
                  // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
                  // is no longer required.
                  result = prod0 * inverse;
                  return result;
              }
          }
          /**
           * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
           */
          function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
              uint256 result = mulDiv(x, y, denominator);
              if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
                  result += 1;
              }
              return result;
          }
          /**
           * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
           *
           * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
           */
          function sqrt(uint256 a) internal pure returns (uint256) {
              if (a == 0) {
                  return 0;
              }
              // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
              //
              // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
              // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
              //
              // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
              // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
              // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
              //
              // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
              uint256 result = 1 << (log2(a) >> 1);
              // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
              // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
              // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
              // into the expected uint128 result.
              unchecked {
                  result = (result + a / result) >> 1;
                  result = (result + a / result) >> 1;
                  result = (result + a / result) >> 1;
                  result = (result + a / result) >> 1;
                  result = (result + a / result) >> 1;
                  result = (result + a / result) >> 1;
                  result = (result + a / result) >> 1;
                  return min(result, a / result);
              }
          }
          /**
           * @notice Calculates sqrt(a), following the selected rounding direction.
           */
          function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
              unchecked {
                  uint256 result = sqrt(a);
                  return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
              }
          }
          /**
           * @dev Return the log in base 2, rounded down, of a positive value.
           * Returns 0 if given 0.
           */
          function log2(uint256 value) internal pure returns (uint256) {
              uint256 result = 0;
              unchecked {
                  if (value >> 128 > 0) {
                      value >>= 128;
                      result += 128;
                  }
                  if (value >> 64 > 0) {
                      value >>= 64;
                      result += 64;
                  }
                  if (value >> 32 > 0) {
                      value >>= 32;
                      result += 32;
                  }
                  if (value >> 16 > 0) {
                      value >>= 16;
                      result += 16;
                  }
                  if (value >> 8 > 0) {
                      value >>= 8;
                      result += 8;
                  }
                  if (value >> 4 > 0) {
                      value >>= 4;
                      result += 4;
                  }
                  if (value >> 2 > 0) {
                      value >>= 2;
                      result += 2;
                  }
                  if (value >> 1 > 0) {
                      result += 1;
                  }
              }
              return result;
          }
          /**
           * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
           * Returns 0 if given 0.
           */
          function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
              unchecked {
                  uint256 result = log2(value);
                  return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
              }
          }
          /**
           * @dev Return the log in base 10, rounded down, of a positive value.
           * Returns 0 if given 0.
           */
          function log10(uint256 value) internal pure returns (uint256) {
              uint256 result = 0;
              unchecked {
                  if (value >= 10 ** 64) {
                      value /= 10 ** 64;
                      result += 64;
                  }
                  if (value >= 10 ** 32) {
                      value /= 10 ** 32;
                      result += 32;
                  }
                  if (value >= 10 ** 16) {
                      value /= 10 ** 16;
                      result += 16;
                  }
                  if (value >= 10 ** 8) {
                      value /= 10 ** 8;
                      result += 8;
                  }
                  if (value >= 10 ** 4) {
                      value /= 10 ** 4;
                      result += 4;
                  }
                  if (value >= 10 ** 2) {
                      value /= 10 ** 2;
                      result += 2;
                  }
                  if (value >= 10 ** 1) {
                      result += 1;
                  }
              }
              return result;
          }
          /**
           * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
           * Returns 0 if given 0.
           */
          function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
              unchecked {
                  uint256 result = log10(value);
                  return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
              }
          }
          /**
           * @dev Return the log in base 256, rounded down, of a positive value.
           * Returns 0 if given 0.
           *
           * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
           */
          function log256(uint256 value) internal pure returns (uint256) {
              uint256 result = 0;
              unchecked {
                  if (value >> 128 > 0) {
                      value >>= 128;
                      result += 16;
                  }
                  if (value >> 64 > 0) {
                      value >>= 64;
                      result += 8;
                  }
                  if (value >> 32 > 0) {
                      value >>= 32;
                      result += 4;
                  }
                  if (value >> 16 > 0) {
                      value >>= 16;
                      result += 2;
                  }
                  if (value >> 8 > 0) {
                      result += 1;
                  }
              }
              return result;
          }
          /**
           * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
           * Returns 0 if given 0.
           */
          function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
              unchecked {
                  uint256 result = log256(value);
                  return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
              }
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)
      pragma solidity ^0.8.0;
      /**
       * @dev Standard signed math utilities missing in the Solidity language.
       */
      library SignedMath {
          /**
           * @dev Returns the largest of two signed numbers.
           */
          function max(int256 a, int256 b) internal pure returns (int256) {
              return a > b ? a : b;
          }
          /**
           * @dev Returns the smallest of two signed numbers.
           */
          function min(int256 a, int256 b) internal pure returns (int256) {
              return a < b ? a : b;
          }
          /**
           * @dev Returns the average of two signed numbers without overflow.
           * The result is rounded towards zero.
           */
          function average(int256 a, int256 b) internal pure returns (int256) {
              // Formula from the book "Hacker's Delight"
              int256 x = (a & b) + ((a ^ b) >> 1);
              return x + (int256(uint256(x) >> 255) & (a ^ b));
          }
          /**
           * @dev Returns the absolute unsigned value of a signed value.
           */
          function abs(int256 n) internal pure returns (uint256) {
              unchecked {
                  // must be unchecked in order to support `n = type(int256).min`
                  return uint256(n >= 0 ? n : -n);
              }
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.9.0) (utils/ShortStrings.sol)
      pragma solidity ^0.8.8;
      import "./StorageSlot.sol";
      // | string  | 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA   |
      // | length  | 0x                                                              BB |
      type ShortString is bytes32;
      /**
       * @dev This library provides functions to convert short memory strings
       * into a `ShortString` type that can be used as an immutable variable.
       *
       * Strings of arbitrary length can be optimized using this library if
       * they are short enough (up to 31 bytes) by packing them with their
       * length (1 byte) in a single EVM word (32 bytes). Additionally, a
       * fallback mechanism can be used for every other case.
       *
       * Usage example:
       *
       * ```solidity
       * contract Named {
       *     using ShortStrings for *;
       *
       *     ShortString private immutable _name;
       *     string private _nameFallback;
       *
       *     constructor(string memory contractName) {
       *         _name = contractName.toShortStringWithFallback(_nameFallback);
       *     }
       *
       *     function name() external view returns (string memory) {
       *         return _name.toStringWithFallback(_nameFallback);
       *     }
       * }
       * ```
       */
      library ShortStrings {
          // Used as an identifier for strings longer than 31 bytes.
          bytes32 private constant _FALLBACK_SENTINEL = 0x00000000000000000000000000000000000000000000000000000000000000FF;
          error StringTooLong(string str);
          error InvalidShortString();
          /**
           * @dev Encode a string of at most 31 chars into a `ShortString`.
           *
           * This will trigger a `StringTooLong` error is the input string is too long.
           */
          function toShortString(string memory str) internal pure returns (ShortString) {
              bytes memory bstr = bytes(str);
              if (bstr.length > 31) {
                  revert StringTooLong(str);
              }
              return ShortString.wrap(bytes32(uint256(bytes32(bstr)) | bstr.length));
          }
          /**
           * @dev Decode a `ShortString` back to a "normal" string.
           */
          function toString(ShortString sstr) internal pure returns (string memory) {
              uint256 len = byteLength(sstr);
              // using `new string(len)` would work locally but is not memory safe.
              string memory str = new string(32);
              /// @solidity memory-safe-assembly
              assembly {
                  mstore(str, len)
                  mstore(add(str, 0x20), sstr)
              }
              return str;
          }
          /**
           * @dev Return the length of a `ShortString`.
           */
          function byteLength(ShortString sstr) internal pure returns (uint256) {
              uint256 result = uint256(ShortString.unwrap(sstr)) & 0xFF;
              if (result > 31) {
                  revert InvalidShortString();
              }
              return result;
          }
          /**
           * @dev Encode a string into a `ShortString`, or write it to storage if it is too long.
           */
          function toShortStringWithFallback(string memory value, string storage store) internal returns (ShortString) {
              if (bytes(value).length < 32) {
                  return toShortString(value);
              } else {
                  StorageSlot.getStringSlot(store).value = value;
                  return ShortString.wrap(_FALLBACK_SENTINEL);
              }
          }
          /**
           * @dev Decode a string that was encoded to `ShortString` or written to storage using {setWithFallback}.
           */
          function toStringWithFallback(ShortString value, string storage store) internal pure returns (string memory) {
              if (ShortString.unwrap(value) != _FALLBACK_SENTINEL) {
                  return toString(value);
              } else {
                  return store;
              }
          }
          /**
           * @dev Return the length of a string that was encoded to `ShortString` or written to storage using {setWithFallback}.
           *
           * WARNING: This will return the "byte length" of the string. This may not reflect the actual length in terms of
           * actual characters as the UTF-8 encoding of a single character can span over multiple bytes.
           */
          function byteLengthWithFallback(ShortString value, string storage store) internal view returns (uint256) {
              if (ShortString.unwrap(value) != _FALLBACK_SENTINEL) {
                  return byteLength(value);
              } else {
                  return bytes(store).length;
              }
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.9.0) (utils/StorageSlot.sol)
      // This file was procedurally generated from scripts/generate/templates/StorageSlot.js.
      pragma solidity ^0.8.0;
      /**
       * @dev Library for reading and writing primitive types to specific storage slots.
       *
       * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
       * This library helps with reading and writing to such slots without the need for inline assembly.
       *
       * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
       *
       * Example usage to set ERC1967 implementation slot:
       * ```solidity
       * contract ERC1967 {
       *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
       *
       *     function _getImplementation() internal view returns (address) {
       *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
       *     }
       *
       *     function _setImplementation(address newImplementation) internal {
       *         require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
       *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
       *     }
       * }
       * ```
       *
       * _Available since v4.1 for `address`, `bool`, `bytes32`, `uint256`._
       * _Available since v4.9 for `string`, `bytes`._
       */
      library StorageSlot {
          struct AddressSlot {
              address value;
          }
          struct BooleanSlot {
              bool value;
          }
          struct Bytes32Slot {
              bytes32 value;
          }
          struct Uint256Slot {
              uint256 value;
          }
          struct StringSlot {
              string value;
          }
          struct BytesSlot {
              bytes value;
          }
          /**
           * @dev Returns an `AddressSlot` with member `value` located at `slot`.
           */
          function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
              /// @solidity memory-safe-assembly
              assembly {
                  r.slot := slot
              }
          }
          /**
           * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
           */
          function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
              /// @solidity memory-safe-assembly
              assembly {
                  r.slot := slot
              }
          }
          /**
           * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
           */
          function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
              /// @solidity memory-safe-assembly
              assembly {
                  r.slot := slot
              }
          }
          /**
           * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
           */
          function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
              /// @solidity memory-safe-assembly
              assembly {
                  r.slot := slot
              }
          }
          /**
           * @dev Returns an `StringSlot` with member `value` located at `slot`.
           */
          function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
              /// @solidity memory-safe-assembly
              assembly {
                  r.slot := slot
              }
          }
          /**
           * @dev Returns an `StringSlot` representation of the string storage pointer `store`.
           */
          function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
              /// @solidity memory-safe-assembly
              assembly {
                  r.slot := store.slot
              }
          }
          /**
           * @dev Returns an `BytesSlot` with member `value` located at `slot`.
           */
          function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
              /// @solidity memory-safe-assembly
              assembly {
                  r.slot := slot
              }
          }
          /**
           * @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
           */
          function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
              /// @solidity memory-safe-assembly
              assembly {
                  r.slot := store.slot
              }
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol)
      pragma solidity ^0.8.0;
      import "./math/Math.sol";
      import "./math/SignedMath.sol";
      /**
       * @dev String operations.
       */
      library Strings {
          bytes16 private constant _SYMBOLS = "0123456789abcdef";
          uint8 private constant _ADDRESS_LENGTH = 20;
          /**
           * @dev Converts a `uint256` to its ASCII `string` decimal representation.
           */
          function toString(uint256 value) internal pure returns (string memory) {
              unchecked {
                  uint256 length = Math.log10(value) + 1;
                  string memory buffer = new string(length);
                  uint256 ptr;
                  /// @solidity memory-safe-assembly
                  assembly {
                      ptr := add(buffer, add(32, length))
                  }
                  while (true) {
                      ptr--;
                      /// @solidity memory-safe-assembly
                      assembly {
                          mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
                      }
                      value /= 10;
                      if (value == 0) break;
                  }
                  return buffer;
              }
          }
          /**
           * @dev Converts a `int256` to its ASCII `string` decimal representation.
           */
          function toString(int256 value) internal pure returns (string memory) {
              return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMath.abs(value))));
          }
          /**
           * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
           */
          function toHexString(uint256 value) internal pure returns (string memory) {
              unchecked {
                  return toHexString(value, Math.log256(value) + 1);
              }
          }
          /**
           * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
           */
          function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
              bytes memory buffer = new bytes(2 * length + 2);
              buffer[0] = "0";
              buffer[1] = "x";
              for (uint256 i = 2 * length + 1; i > 1; --i) {
                  buffer[i] = _SYMBOLS[value & 0xf];
                  value >>= 4;
              }
              require(value == 0, "Strings: hex length insufficient");
              return string(buffer);
          }
          /**
           * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
           */
          function toHexString(address addr) internal pure returns (string memory) {
              return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
          }
          /**
           * @dev Returns true if the two strings are equal.
           */
          function equal(string memory a, string memory b) internal pure returns (bool) {
              return keccak256(bytes(a)) == keccak256(bytes(b));
          }
      }
      // SPDX-License-Identifier: UNLICENSED
      // Copyright 2025 US Fintech LLC and DelNorte Holdings.
      // 
      // Permission to use, copy, modify, or distribute this software is strictly prohibited
      // without prior written consent from either copyright holder.
      // 
      // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
      // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
      // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
      // CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE,
      // ARISING FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
      // OFFICIAL DEL NORTE NETWORK COMPONENT 
      // Provides immediate membership access to platform at different levels. 
      // Required Non US or accredited US registration to swap for DTV token. Registration available within 180 days per terms.delnorte.io . 
      // This token is minimally tested. Use at your own risk.   
      // Designed by Ken Silverman as part of his ElasticTreasury (HUB and SPOKE), PeerTreasury and Controller model.  
      // This deployment is for Trueviewchain Inc. a Panama entity and Del Norte El Salvador S.A  a subsidiary of Del Norte Holdings, Delaware USA.  
      // Permission to change metadata stored on blockchain explorers granted to Del Norte Holdings, DE only.
      // Compilation help from Maleeha Naveed. Deployed by Maleeha Naveed. May 5th, 2025
      pragma solidity 0.8.29;
      import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
      import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
      import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";
      import "./ElasticTreasuryHub.sol";
      import "./PeerTreasury.sol";
      /// @author Ken Silverman
      /// @notice DelNorte Club Token is the functional token of DelNorte El Salvador S.A. and all nations of the Del Norte Network.
      contract DelNorteClubToken is ERC20, ERC20Permit, ERC20Burnable, ElasticTreasuryHub, PeerTreasury {
          
          event RegistrarEvent(address indexed registrar, uint256 blockNumber, string action, uint256 numUsers);
          struct WhitelistEntry {
              address user;
              string note;
              bool isUS;
          }
          // 825,000,000 DTVC all tokens less DTVX distributed to founders and early purchasers.
          // 1,000,000,000 DTVC total supply. (plus one for distinguishing from DTV for bytecode verification)
          //175,000,000 remaining to be burned when DTVX is provided.
          uint256 public constant MAX_SUPPLY = 1_000_000_001 * 10**18;
          bool public gatingActive = false;
          mapping(address => string) public whitelistedUsers;
          mapping(address => bool)   public isUserRestricted;
          mapping(address => bool)   public whitelistedUsersIsUSA;
          address[] public whitelistedUserList;
          modifier onlyTokenExecutives() {
              require(IController(controller).isOfficialEntity("TokenAdmin",msg.sender),"NotTknExec");
              _;
          }
          // if masterHub is 00000000000 it means SELF (pass it up the line)
          // address baseTreasuryAddress, address intialAdmin
          /// @author Ken Silverman
          /// @notice DelNorte Club Token is the membership club token for DelNorte El Salvador S.A.
          /// @dev This token is not functional but does provide immediate access tot the dev.delnorte.io development platform
          /// @dev To monitor and help mold the state of DelNorte development progress in real time.
          /// @dev  Regsitration under US securities law REG S. and REG D. 506C required for swap to DTV.
          constructor(address _controller, address initialAdmin)
              ERC20("DelNorte Club Token", "DTVC")
              ERC20Permit("DelNorte Club Token")
              ElasticTreasuryHub(_controller, "DelNorteClubToken")
              PeerTreasury(_controller)
          {
              require(_controller != address(0), "ctrolr is zero");
              //bool areLords = IController(_controller).isOfficialEntity("TreasuryAdmin",initialAdmin);
              //require(areLords, "First token admin (multi-sig execs) must be initial Controller TreasAdmin.");
              _mint(address(this), MAX_SUPPLY);
              // Can we as admins be registrar? Yes if we provide KYC
              // Controller must mark this SC as SmartContract already
              // These calls assume controller is of type IController or castable
              // AT LEAST ONe REGISTRAR MUST BE ADDED By a CONTROLLER TREASURYADMIN or nobody wil be able to be whitelisted!
              // IController(controller).addOfficialEntity("Registrar", initialAdmin,"ExecGrp", "acting Registrar");
              IController(controller).addOfficialEntity("TokenAdmin", initialAdmin,"ExecGrp", "initTknAdmin");
          }
          // no LP or other retrieval to any non-registered user (except by executive manual add to ReleaseManager). 
          //  USACCRED or Non-US can register.  but only NON-US reg can receive  (except via ReleaseManager)
          //  LP considerations: in the common flow:
          //
          // For user selling tokens (input):
          //   In transferFrom(sender, recipient, amount):
          //     sender     = user address
          //     recipient  = pool address <== not whitelisted not officialEntity smart contract
          //     msg.sender = router contract <== Uniswap V2, V3, sushiSwap addresses etc ... are whitelisted officialEntity smart contracts
          //            so anyone can send. (cannot have unless whitelisted or manually put into trelease manager though)
          //
          // For user buying tokens (output):
          //   In transfer(recipient, amount):
          //   msg.sender = pool address <== not whitelisted not officialEntity smart contract
          //   recipient = user address <== is receiver nonUS? yes! all good, no!, Will fail from any LP. 
          function isRestricted(address sender, address receiver) public view returns (bool) {
              // Did sender have an address in an unauthorized wrapped token or otherwise become blacklisted for life?  
              // Not today!  BANNED! blacklisted = whitelisted + restricted sender does NOT need to be whitelisted, but cannot be restricted 
              // That's becaseu a sender cant get it in the first place unless they are whitelisted anyway.
              if (bytes(whitelistedUsers[sender]).length > 0 && isUserRestricted[sender]) {
                  return true;
              }
              if (!gatingActive || isUserWhitelistedNonUSA(receiver)) {
                  return false;
              }
              // What if user is US?  How do we guarantee early US adopters get their exact owed tokens but new US users cannot buy or receive? 
              // and old US users cannot buy or sell anymore then their old SAFT agreement specifies?
              // WE manually add all early purchasers to the ReleaseManager which itself is whitelisted as an official smart contract
              // Therefore, the US user must be added to the ReleaseManager manaually or via a swap from another token like DTVC
              // The line above prevents the US user from receiving except by a whitelisted contract (ReleaseManager) 
              // The next line allows ANY official smart contract in our system to permit receiving of DTV 
              // Our release manager is one such contract an the LP router is another.
              // This is the only way to prevent US users from buying or receiving any more tokens then the ReleaseManager will grant them. 
              // They cannot sell accept to the LP. The LP sell is allowed becasue transferFrom msg.sender is the whitelisted router contract.
              // (see next line granting permission as the fourth officialEntity)
              // Any holder can sell to any LP on any official router.  If they hold they must be allowed to sell under:
              // REG S or REG D (we have proof they held for more than 12 months).
              // All holders/receivers are already registered, held for 12 months or are confirmed non-US residents.
              return !(IController(controller).isOfficialQuadrupleEntityFast(KECCAK_TREASURY_ADMIN, sender,KECCAK_SMART_CONTRACT, sender,KECCAK_SMART_CONTRACT, receiver,KECCAK_SMART_CONTRACT,msg.sender,false));
          }
          function isUnrestrictedWhitelistedUser(address user) public view returns (bool) {
              return bytes(whitelistedUsers[user]).length > 0 && !isUserRestricted[user];
          }
          /// @notice Returns true if `user` is whitelisted and not flagged US
          // must be TRUE for DTVC swaps.  See TokenSaleAndSwap.sol
          function isUserWhitelistedNonUSA(address user) public view returns (bool) {
              // must have a registration note + US‑flag == 0
              return !gatingActive || (bytes(whitelistedUsers[user]).length > 0 && whitelistedUsersIsUSA[user] == false && !isUserRestricted[user]);
          }
          function getUserRegistrationStatus(address user, bool notGatedOverride) public view returns (uint8) {
              if (notGatedOverride && !gatingActive) {
                  return 3; // don't care if registered or not
              }
              if (bytes(whitelistedUsers[user]).length == 0) {
                  return 0; // Not registered
              }
              if (whitelistedUsersIsUSA[user]) {
                  return 1; // US
              }
              return 2; // Non-US
          }
          function transfer(address recipient, uint256 amount) public override returns (bool) {
              require(!isRestricted(msg.sender, recipient), "restricted");
              return super.transfer(recipient, amount);
          }
          function transferFrom(address sender, address recipient, uint256 amount) public override returns (bool) {
              require(!isRestricted(sender, recipient), "restricted");
              return super.transferFrom(sender, recipient, amount);
          }
          function setGating(bool active) external onlyTokenExecutives {
              gatingActive = active;
          }
          function getAllWhitelistedUsers() external view returns (address[] memory) {
             return whitelistedUserList;
          }
          // NOT just for sales but for regular users who are registered
          function addWhitelistedUser(address user, string memory registrationNote, bool isUS) external {
               require(IController(controller).isOfficialEntity("Registrar", msg.sender), "Not Regstrar");
               if (bytes(whitelistedUsers[user]).length == 0) {
                   whitelistedUserList.push(user);
               }
               whitelistedUsers[user] = registrationNote;
               whitelistedUsersIsUSA[user] = isUS;                     // added: default to non‑US
               emit RegistrarEvent(msg.sender, block.number, "Added/updated User", 1);
          }
          function addWhitelistedUsers(WhitelistEntry[] calldata entries) external {
              require(IController(controller).isOfficialEntity("Registrar", msg.sender), "Not Regstrar");
              uint256 numUsers = 0;
              for (uint256 i = 0; i < entries.length; ++i) {
                  address user = entries[i].user;
                  if (bytes(whitelistedUsers[user]).length == 0) {
                      whitelistedUserList.push(user);
                      numUsers++;
                  }
                  whitelistedUsers[user] = entries[i].note;
                  whitelistedUsersIsUSA[user] = entries[i].isUS;                     // added: default to non‑US
              }
              emit RegistrarEvent(msg.sender, block.number, "Added Users", numUsers);
          }
          // Only Registrar can delete a whitelisted user, and ONLY if a mistake was made,
          // we dont want to delete a user for accountability we just want to restrict them if necassary for accountability
          // can remove this method if needed
          //function deleteWhitelistedUser(address user) external {
          //    require(IController(controller).isOfficialEntity("Registrar",msg.sender));
          //    delete whitelistedUsers[user];
          //    emit RegistrarEvent(msg.sender, block.number, "Deleted a User", 1);
          //}
          function restrictWhitelistedUsers(address[] calldata users) external {
              require(IController(controller).isOfficialEntity("Registrar", msg.sender), "Not Regstrar");
              uint256 numUsers = 0;
              for (uint256 i = 0; i < users.length; ++i) {
                  address user = users[i];
                  isUserRestricted[user] = true;
                  numUsers++;
              }
              emit RegistrarEvent(msg.sender, block.number, "Users Rstrcted", numUsers);
          }
          function reactivateWhitelistedUsers(address[] calldata users) external {
              require(IController(controller).isOfficialEntity("Registrar", msg.sender), "Not Regstrar");
              uint256 numUsers = 0;
              for (uint256 i = 0; i < users.length; ++i) {
                  address user = users[i];
                  if (bytes(whitelistedUsers[user]).length != 0 && isUserRestricted[user]) {
                      isUserRestricted[user] = false;
                      numUsers++;
                  }
              }
              emit RegistrarEvent(msg.sender, block.number, "Users Rctivted", numUsers);
          }
          function getActiveWhitelistedUsers() external view returns (address[] memory) {
              uint256 activeCount = 0;
              for (uint256 i = 0; i < whitelistedUserList.length; ++i) {
                  if (!isUserRestricted[whitelistedUserList[i]]) {
                      activeCount++;
                  }
              }
              address[] memory activeUsers = new address[](activeCount);
              uint256 j = 0;
              for (uint256 i = 0; i < whitelistedUserList.length; ++i) {
                  address user = whitelistedUserList[i];
                  if (!isUserRestricted[user]) {
                      activeUsers[j++] = user;
                  }
              }
              return activeUsers;
          }
          /**
           * @notice Allows a user to self-register (whitelist themselves) using an off-chain signature
           *         provided by an approved Registrar. The registrar signs a hashed message consisting of:
           *
           *         keccak256(abi.encodePacked(userAddress + "_" + thisTokencontractAddress))
           *
           *         - The registration note (string) passed to this function is NOT part of the signed message.
           *           It is for reference and display only and can be spoofed. All actual verification (e.g., KYC,
           *           ISO country code, etc.) is done off-chain by registrar, details stored off-chain by address.
           *
           *         - The registrar's address must be registered in the controller as an official entity
           *           of type "Registrar".     registrarSignature = regKey
           *         - The user (msg.sender) pays the gas and must be the subject of the signed message.
           */
           // @rawMessage = user + thisContract + "0"/"1"   1 = USACCRDTD  (user and thiscontract are fixed Length 20 bytes, no spearator needed)
           // US accredited are NOT allowed for PDTV swaps, that is why we store this flag.
           //registrationNote = "isUs
          function whitelistUserWithRegKey(address registrarAddress, bool isUS, bytes calldata registrarSignedMessage,  string calldata registrationNote) external {
              require(bytes(whitelistedUsers[msg.sender]).length == 0, "User already registered");
              // build raw message = user + thisContract + "0"/"1"    (user and thiscontract are fixed Length 20 bytes, no spearator needed)
              bytes memory rawMessage = abi.encodePacked(
                  msg.sender,
                  address(this),
                  isUS ? "1" : "0"
              );
              bytes32 messageHash = keccak256(rawMessage);
              // Ethereum signed message format
              bytes32 ethSignedMessageHash = keccak256(
                  abi.encodePacked("\\x19Ethereum Signed Message:\
      32", messageHash)
              );
              // Recover signer
              address recoveredSigner = recoverSigner(ethSignedMessageHash, registrarSignedMessage);
              require(recoveredSigner == registrarAddress,   "Sig not from registrar");
              require(IController(controller).isOfficialEntity("Registrar", registrarAddress),"invalid registrar");
              // Whitelist the user
              whitelistedUsers[msg.sender] = registrationNote;
              whitelistedUserList.push(msg.sender);
              whitelistedUsersIsUSA[msg.sender] = isUS;
              emit RegistrarEvent(registrarAddress, block.number, "User Self-Regstd", 1);
          }
          // Internal helper to recover the signer from signature
          function recoverSigner(bytes32 hash, bytes memory signature) internal pure returns (address) {
              require(signature.length == 65, "Invalid sig len");
              bytes32 r;
              bytes32 s;
              uint8 v;
              assembly {
                  r := mload(add(signature, 32))
                  s := mload(add(signature, 64))
                  v := byte(0, mload(add(signature, 96)))
              }
              if (v < 27) {
                  v += 27;
              }
              require(v == 27 || v == 28, "Invalid v val");
              return ecrecover(hash, v, r, s);
          }
      }// SPDX-License-Identifier: UNLICENSED
      // Copyright 2025 US Fintech LLC and DelNorte Holdings.
      // 
      // Permission to use, copy, modify, or distribute this software is strictly prohibited
      // without prior written consent from either copyright holder.
      // 
      // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
      // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
      // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
      // CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE,
      // ARISING FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
      // OFFICIAL DEL NORTE NETWORK COMPONENT 
      // Designed and coded by: Ken Silverman
      // Compiled and deployed by Maleeha Naveed on behalf of Del Norte
      pragma solidity 0.8.29;
      import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
      import "../interfaces/IController.sol"; //   // ⇐ for method signatures only to allow casting during compilation.
      import "../interfaces/IElasticTreasurySpoke.sol";
      import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
      /// @title ElasticTreasuryHub
      /// @notice A contract for managing a treasury of ERC20 tokens and ETH
      /// @dev This contract is used to manage a treasury of ERC20 tokens and ETH
      /// @author Ken Silverman
      abstract contract ElasticTreasuryHub {
          using SafeERC20 for IERC20;
          address public controller;
          bytes32 public constant KECCAK_TREASURY_ADMIN = keccak256(bytes("TreasuryAdmin"));
          bytes32 public constant KECCAK_SMART_CONTRACT = keccak256(bytes("SmartContract"));
          uint256 constant DUST = 0.01 ether;
          event HubTreasuryEvent(address adminCaller, string msg, bool success,address smartContractTarget, uint256 amount, address tokenOrZero);
          event HubExecutiveTokensWithdrawalEvent(address adminCaller, address receiver, uint256 amt, string note, address tokenSC);
          event HubExecutiveEthWithdrawalEvent(address adminCaller, address receiver, uint256 amt, string note);
          event HubExternalReceive(address indexed sender,address indexed origin,uint256 amount,uint256 blockNumber,uint256 gasLeft);
          struct SingleCoinTreasuryState {
              string label;
              uint256 totalTransferred;
              uint256 totalReclaimed;
              uint256 totalTimesTransferred;
              uint256 totalTimesReclaimed;
              uint256[] failedReclaimAttemptAmounts;
              uint256[] bouncedReclaimAmounts;
          }
          struct AllCoinTreasury {
              mapping(address => SingleCoinTreasuryState) tokenTreasury;
              address[] tokenTreasuryKeys;
              SingleCoinTreasuryState ethTreasury;
              string label;
          }
          mapping(address => AllCoinTreasury) private treasury;
          address[] private treasuryKeys;
           // elastic treasury events with gas that fail without reversion or require
          // controller reference is NOW stored in Recoverable
         // if somehow anyone managed a call to a spoke reclaim, (in theory they can't) but if they did,  then
         // that SC would have the authority to call the reclaim here as a SC exec.
         // therefore we require BOTH SC as spoke AND tx.origin to be Treas on reclaim
          modifier onlyTreas()  {
              require(IController(controller).isOfficialEntityFast(KECCAK_TREASURY_ADMIN,msg.sender), "Unauth access");
              _;  // run the code block referencing this modifier
          }
          // masterHub can no longer exist because BaseTreasury is a SINGLE entity, not inherited anymore.
          // in other words, the ENTIRE Del Norte system uses ONE Base Treasury instance as CONTROL PANEL.
          // Controller already has an initial admin, so do not add any more here.
          // Admin can call addEntity to add more. (controller has option to receive more in init() but not necessary.
          constructor(address _controllerAddress, string memory contractName) {
                controller = _controllerAddress;
                IController(_controllerAddress).init(address(this), contractName);
          }
          // EMERGENCY withdraw for admin controlled DIRECT withdrawals to a PERSON (not a smart contract)
          // this can ONLY happen for example, when the company wants to send some USDC or ETH from the Sales contract
          // to an EXECUTIVE of the company for the purposes of exchanging to USDC, as an example.
          // Such USDC can be spent on a capital expense or manually passed into the vesting pool.
          // ONLY HUBS can do this. For example, only contracts GENERATING or expected to RECEIVE
          // ETH or tokens from an external event (not from a HUB)  should be declared as HUBS.
          // EXCEPTIONS: send tokens to a launchpad or other noted entity can be a SC.
          function executiveWithdrawETH(address personAddress, uint256 amt, string memory note) external onlyTreas {
              require(address(this).balance >= amt, "Insufficient ETH balance");
              (bool success, ) = payable(personAddress).call{value: amt}("");
              require(success, "ETH transfer failed");
              emit HubExecutiveEthWithdrawalEvent(msg.sender, personAddress, amt, note);
          }
          function executiveWithdrawTokens(address personAddress, uint256 amt, address tokenSCAddress, string memory note) external onlyTreas {
              require(tokenSCAddress != address(0), "Invalid tok addr");
              IERC20 token = IERC20(tokenSCAddress);
              require(token.balanceOf(address(this)) >= amt, "Insuffic tok bal");
              bool success = token.transfer(personAddress, amt);
              require(success, "Token transfer failed");
              emit HubExecutiveTokensWithdrawalEvent(msg.sender, personAddress, amt, note, tokenSCAddress);
          }
          /// @param SCAddress is the spoke address transfer to    
          /// @param tokenAddress is the transfer from treasury token address
          /// @param allCoinLabel is the label of the spoke (contractName preferred)
          /// @param tokenLabel is the label of the token   (symbol of token prefferred)
          /// @param amount is the amount to transfer
          // NEW: Add this import at the top of your contract file
          function treasuryTransfer(address SCAddress, string memory allCoinLabel, address tokenAddress, string memory tokenLabel,uint256 amount) external onlyTreas returns (bool) {
              // NEW: Basic validation checks with specific error messages
              require(SCAddress != address(0), "SCAddress cannot be zero address");
              require(tokenAddress != address(0), "TokenAddress cannot be zero address");
              require(amount > 0, "Amount must be greater than zero");
              require(isContract(SCAddress), "Invalid contract: SCAddress is not a contract");
              require(bytes(tokenLabel).length > 0, "Token label cannot be empty");
              // Add to smartContractAdmins array if not already included
              require(IController(controller).isOfficialEntity("SmartContract",SCAddress), "Spoke not yet part of SC network.");
              // SELF is added as official SC in parent constructor - and same for spoke
              AllCoinTreasury storage coinTreasury = treasury[SCAddress];
              if (bytes(coinTreasury.label).length != 0) {
                  require(keccak256(bytes(coinTreasury.label)) == keccak256(bytes(allCoinLabel)),"AllCoinTreasury label mismatch");
              }
              else {
                  coinTreasury.label = allCoinLabel;
                  treasuryKeys.push(SCAddress); // NEW: add new spoke key
              }
              SingleCoinTreasuryState storage tokenState = coinTreasury.tokenTreasury[tokenAddress];
              // If this is a new token (label is empty), add it to the keys array
              if (bytes(tokenState.label).length == 0) {
                  // This is a new token, add it to the keys array
                  coinTreasury.tokenTreasuryKeys.push(tokenAddress);
                  // Also set the label if provided
                  if (bytes(tokenLabel).length != 0) {
                      tokenState.label = tokenLabel;
                  }
              } 
              else if (bytes(tokenLabel).length != 0) {
                  // Token exists, just verify the label matches
                  require(keccak256(bytes(tokenState.label)) == keccak256(bytes(tokenLabel)), "Token label mismatch");
              }
              // NEW: Check token balance before attempting transfer
              uint256 contractBalance = IERC20(tokenAddress).balanceOf(address(this));
              require(contractBalance >= amount, "Insufficient token balance for transfer");
              // NEW: Update state before transfer (prevent reentrancy)
              tokenState.totalTransferred += amount;
              tokenState.totalTimesTransferred++;
              //bytes32 labelHash = keccak256(bytes(tokenLabel));
              //if (labelHash == USDT_LABEL_UPPER || labelHash == USDT_LABEL_LOWER) {
              //    (bool success, ) = tokenAddress.call(abi.encodeWithSelector(0xa9059cbb, SCAddress, amount));
              //  require(success, "USDT transfer failed");
              //}
              //else {
              // For other tokens, use regular transfer with boolean check
              IERC20(tokenAddress).safeTransfer(SCAddress, amount);
              //}
              // Use low-level call to treasuryReceive - most gas efficient
              (bool success,) = SCAddress.call(abi.encodeWithSelector(bytes4(keccak256("treasuryReceive(address,uint256)")),SCAddress,amount));
              // Check success and provide meaningful error if possible
              require(success, "Target not spoke or bad TR");
              emit HubTreasuryEvent(msg.sender, "Amount transferred to spoke", true, SCAddress, amount, tokenAddress);
              return true;
          }
          /// @param SCAddress is the spoke address transfer to    
          /// @param allCoinLabel is the label of the spoke (contractName preferred)
          /// @param ethLabel is the label for ETH   ("ETH")
          /// @param amount is the amount to transfer
          function treasuryTransferETH(address payable SCAddress, string memory allCoinLabel, string memory ethLabel, uint256 amount) external onlyTreas returns (bool) {
              // NEW: Basic validation checks with specific error messages
              require(SCAddress != address(0), "SCAddress cannot be zero address");
              require(amount > 0, "Amount must be greater than zero");
              require(isContract(SCAddress), "Invalid contract: SCAddress is not a contract");
              // NEW: Check if spoke is registered before proceeding
              require(IController(controller).isOfficialEntity("SmartContract", SCAddress), "Spoke not yet part of SC network.");
              AllCoinTreasury storage coinTreasury = treasury[SCAddress];
              if (bytes(coinTreasury.label).length != 0) {
                  require(keccak256(bytes(coinTreasury.label)) == keccak256(bytes(allCoinLabel)),"AllCoinTreas label mismatch");
              }
              else {
                  coinTreasury.label = allCoinLabel;
                  // NEW: Add key if it's a new treasury
                  treasuryKeys.push(SCAddress);
              }
              SingleCoinTreasuryState storage ethState = coinTreasury.ethTreasury;
              if (bytes(ethState.label).length != 0 && bytes(ethLabel).length != 0) {
                  require(keccak256(bytes(ethState.label)) == keccak256(bytes(ethLabel)),"ETH label mismatch");
              }
              else if (bytes(ethState.label).length == 0 && bytes(ethLabel).length != 0) {
                  ethState.label = ethLabel;
              }
              // Check ETH balance
              require(address(this).balance >= amount, "Insufficient ETH balance");
              // NEW: Update state before transfer (prevent reentrancy)
              ethState.totalTransferred += amount;
              ethState.totalTimesTransferred++;
              // NEW: Use try-catch for error handling
              (bool success,) = SCAddress.call{value: amount}(abi.encodeWithSelector(bytes4(keccak256("treasuryReceiveETH()"))));
              require(success, "Target not spoke or bad TR ETH");
              // ETH transfer succeeded
              //} catch Error(string memory reason) {
              //    // Revert state changes on failure
              //    ethState.totalTransferred -= amount;
              //    ethState.totalTimesTransferred--;
              //    emit HubTreasuryEvent(msg.sender, reason, false, SCAddress, amount, address(0));
              //    revert(string(abi.encodePacked("ETH transfer failed: ", reason)));
              //} catch {
              //     // Revert state changes on unknown failure
               //   ethState.totalTransferred -= amount;
               //   ethState.totalTimesTransferred--;
               //   emit HubTreasuryEvent(msg.sender, "Unknown ETH transfer error", false, SCAddress, amount, address(0));
               //   revert("ETH transfer failed: unknown error");
              //}
              emit HubTreasuryEvent(msg.sender, "ETH transferred to SC", true, SCAddress, amount, address(0));
              return true;
          }
      // ETH reclaim receiver
      function treasuryReceiveReclaimedETH() external payable returns (bool) {
          AllCoinTreasury storage coinTreasury = treasury[msg.sender];
          require(bytes(coinTreasury.label).length != 0, "Treasury entry does not exist for caller as SPOKE");
          require(IController(controller).isOfficialDoubleEntity("SmartContract",msg.sender,"TreasuryAdmin",tx.origin,true),
              "Originator must be treas and sender must be an official smart contract.");
          SingleCoinTreasuryState storage ethState = coinTreasury.ethTreasury;
          ethState.totalReclaimed += msg.value;
          ethState.totalTimesReclaimed++;
          emit HubTreasuryEvent(msg.sender, "ETH reclaimed", true,  msg.sender,msg.value,address(0));
          return true;
      }
      // Token reclaim notifier
      function treasuryReceiveReclaimedTokens(address tokenAddress, uint256 amt) external returns (bool) {
          AllCoinTreasury storage coinTreasury = treasury[msg.sender];
          require(bytes(coinTreasury.label).length != 0, "Treasury entry does not exist");
          require(IController(controller).isOfficialDoubleEntity("SmartContract",msg.sender,
                                                                      "TreasuryAdmin",tx.origin,true),
              "Originator must be treas and sender must be an official smart contract.");
          SingleCoinTreasuryState storage tokenState = coinTreasury.tokenTreasury[tokenAddress];
          tokenState.totalReclaimed += amt;
          tokenState.totalTimesReclaimed++;
          emit HubTreasuryEvent(msg.sender, "Token reclaimed", true, address(this),amt,tokenAddress);
          return true;
      }
      function treasuryReclaimRequest(address SCAddress, address tokenAddress, uint256 amount) external onlyTreas returns (bool) {
              uint256 availableToReclaim = treasury[SCAddress].tokenTreasury[tokenAddress].totalTransferred - treasury[SCAddress].tokenTreasury[tokenAddress].totalReclaimed;
              if (amount > treasury[SCAddress].tokenTreasury[tokenAddress].totalTransferred) {
                  treasury[SCAddress].tokenTreasury[tokenAddress].failedReclaimAttemptAmounts.push(amount);
                  emit HubTreasuryEvent(msg.sender, "Reclaim request exceeds total transferred", false, address(this),amount,address(0));
                  return false;
              }
              if (amount > availableToReclaim) {
                  treasury[SCAddress].tokenTreasury[tokenAddress].bouncedReclaimAmounts.push(amount);
                  amount = availableToReclaim - DUST;
                  emit HubTreasuryEvent(msg.sender, "Reclaim request bounce, too high", false, address(this),amount,address(0));
                  return false;
              }
              // if this fails, whole thing will revert, but the idea is if we passed
              // the above checks, than the corresponding state in the spoke should match.
              // In other words a revert should never happen if we get to this point.
              // if it does there is a state mismatch that the web manager will have to
              // warn about for manual reconciliation.
              // eth logs on chain should group msg.sender so when reclaim succeeds the message
              // will follow this event message herein 'request submitted'.  If however,
              // the transfer fails due to rejection at the spoke,
              // the below log entry will never be entered.
              IElasticTreasurySpoke(SCAddress).treasuryReclaim(tokenAddress, amount);
              treasury[SCAddress].tokenTreasury[tokenAddress].totalReclaimed += amount;
              treasury[SCAddress].tokenTreasury[tokenAddress].totalTimesReclaimed++;
              emit HubTreasuryEvent(msg.sender, "Reclaim TOKEN request submitted to SPOKE", true,SCAddress,amount,tokenAddress);
              return true;
          }
          function treasuryReclaimRequestETH(address payable SCAddress, uint256 amount) external onlyTreas returns (bool) {
              uint256 availableToReclaim = treasury[SCAddress].ethTreasury.totalTransferred - treasury[SCAddress].ethTreasury.totalReclaimed;
              if (amount > treasury[SCAddress].ethTreasury.totalTransferred) {
                  treasury[SCAddress].ethTreasury.failedReclaimAttemptAmounts.push(amount);
                  emit HubTreasuryEvent(msg.sender, "ETH Reclaim exceeds total transferred", false,SCAddress,amount,address(0));
                  return false;
              }
              if (amount > availableToReclaim) {
                  treasury[SCAddress].ethTreasury.bouncedReclaimAmounts.push(amount);
                  amount = availableToReclaim - DUST;
                  emit HubTreasuryEvent(msg.sender, "ETH Reclaim request bounce, too high", false,SCAddress,amount,address(0));
                  return false;
              }
              IElasticTreasurySpoke(SCAddress).treasuryReclaimETH(amount);
              treasury[SCAddress].ethTreasury.totalReclaimed += amount;
              treasury[SCAddress].ethTreasury.totalTimesReclaimed++;
              emit HubTreasuryEvent(msg.sender, "ETH reclaim request submitted", true,SCAddress,amount,address(0));
              return true;
          }
          // PURCHASERS on the SALES contract or any other contract which acts as its own HUB
          // should not  accidently get ETH sent to this contract unless the child implements a receive()
          // or fallback or other payable - NOT a good idea (except buy and other planned custom methods) !
          // There should be no way to receive ETH (or tokens if it were possible) outside of SALES or other means
          // Sales contract has its own payable for example after a purchase is verified by whitelist etc ...
          // function customReceiveETH internal virtual payable {  do NOT implement
          function isContract(address account) internal view returns (bool) {
              return account.code.length > 0;
          }
        //  VIEW GETTERS
          struct TokenTreasuryView {
              address token;
              SingleCoinTreasuryState state;
          }
          struct SpokeTreasuryView {
              address spoke;
              string label;
              SingleCoinTreasuryState ethState;
              TokenTreasuryView[] tokenStates;
          }
          
          function getAllSpokeTreasuries() external view returns (SpokeTreasuryView[] memory) {
              uint256 len = treasuryKeys.length;
              SpokeTreasuryView[] memory result = new SpokeTreasuryView[](len);
              for (uint256 i = 0; i < len; i++) {
                  address spoke = treasuryKeys[i];
                  AllCoinTreasury storage coinTreasury = treasury[spoke];
                  uint256 tokenCount = coinTreasury.tokenTreasuryKeys.length;
                  TokenTreasuryView[] memory tokenViews = new TokenTreasuryView[](tokenCount);
                  for (uint256 j = 0; j < tokenCount; j++) {
                      address token = coinTreasury.tokenTreasuryKeys[j];
                      SingleCoinTreasuryState storage tokenState = coinTreasury.tokenTreasury[token];
                      tokenViews[j] = TokenTreasuryView({
                          token: token,
                          state: tokenState
                      });
                  }
                  result[i] = SpokeTreasuryView({
                      spoke: spoke,
                      label: coinTreasury.label,
                      ethState: coinTreasury.ethTreasury,
                      tokenStates: tokenViews
                  });
              }
              return result;
          }
          function getAllCoinSpokeTreasury(address spokeAddress) external view returns (TokenTreasuryView[] memory) {
              AllCoinTreasury storage coinTreasury = treasury[spokeAddress];
              uint256 tokenCount = coinTreasury.tokenTreasuryKeys.length;
              TokenTreasuryView[] memory tokenViews = new TokenTreasuryView[](tokenCount);
              for (uint256 i = 0; i < tokenCount; i++) {
                  address token = coinTreasury.tokenTreasuryKeys[i];
                  SingleCoinTreasuryState storage tokenState = coinTreasury.tokenTreasury[token];
                  tokenViews[i] = TokenTreasuryView({
                      token: token,
                      state: tokenState
                  });
              }
              return tokenViews;
          }
         
              // only HUBS can receive ETH from some remote source (e.g. launchpad escrow contract)
              receive() external payable {
                  require(msg.value > 0, "Zero ETH not allowed");
                  emit HubExternalReceive(
                      msg.sender,     // the immediate caller (e.g. launchpad escrow contract)
                      tx.origin,      // the original EOA that started the transaction (e.g. the user)
                      msg.value,
                      block.number,
                      gasleft()
                  );
              }
      }// SPDX-License-Identifier: Copyright 2025
      // OFFICIAL DEL NORTE NETWORK COMPONENT
      // Designed and coded by: Ken Silverman
      // Compiled by: Maleeha Naveed May 5th, 2025
      pragma solidity 0.8.29;
      import "../interfaces/IController.sol";
      import "../interfaces/IElasticTreasuryHub.sol";
      import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
      import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
      import "./Recoverable.sol";
      /// @title ElasticTreasurySpoke
      /// @notice Spoke contract for ElasticTreasuryHub
      /// @author Ken Silverman
      abstract contract ElasticTreasurySpoke {
          
          using SafeERC20 for IERC20;
          address public immutable controller;
          event SpokeReceivedEth(address sender, uint256 amount);
          event SpokeReceivedTokens(address sender, uint256 amount, address tokenSCAddress);
          event SpokeReclaimEthEvent(address origin, address hubContract, string msg, uint256 amount);
          event SpokeReclaimTokensEvent(address origin, address hubContract, address tokenAddress, string msg, uint256 amount);
          address public immutable hubTokenAddress;
          struct SpokeTreasuryEntry {
              uint256 totalReceived;
              uint256 totalReclaimed;
          }
          mapping(address => SpokeTreasuryEntry) public spokeTreasury; // tokenAddress => SpokeTreasuryEntry
          SpokeTreasuryEntry public spokeTreasuryETH;                   // ETH treasury entry explicitly
          // Controller already has an initial admin, so do not add any more here.
          constructor(address _controller, address _hubTokenAddress, string memory contractName)  {
              controller = _controller;
              IController(controller).init(address(this), contractName);
              hubTokenAddress = _hubTokenAddress;
          }
          function treasuryReceive(address tokenAddress, uint256 amount) external {
              require(msg.sender == hubTokenAddress, "sender must be HUB");
              spokeTreasury[tokenAddress].totalReceived += amount;
              // explicitly tracks ERC20 tokens received from treasuryTransfer
          }
          // ETH transfers automatically trigger receive() only if no data is sent.
          // Here, we're explicitly defining a method (treasuryReceiveETH)
          // that's explicitly called by the hub to transfer ETH and record this separately
          //  treasury-related ETH separately from ONE CALL to THIS METHOD. NO PRIOR TRANSFER!
          function treasuryReceiveETH() external payable {  // ⇐ MUST DIRECTLY RECEIVE NOW
              require(msg.sender == hubTokenAddress,"sender must be HUB");
              // confirm amount actually equals msg.value
              spokeTreasuryETH.totalReceived += msg.value;
              emit SpokeReceivedEth(msg.sender,msg.value);
          }
          // DO NOT USE ⇒ receive() external payable {   // NO NO NO NO receive method! Do NOT allow incoming ETH
          //    emit SpokeReceivedEth(msg.sender,msg.value);  // except by treasRcvETH
          // }
           // ANY address that this contract has deployed as its HUB is 100% trusted.
          // because caller must be hub and HUB is only accessible by TREAS.
          function treasuryReclaim(address tokenAddress, uint256 amount) external returns (bool) {
              require(spokeTreasury[tokenAddress].totalReceived >= spokeTreasury[tokenAddress].totalReclaimed + amount, "Low balance to reclaim on");
              require(hubTokenAddress == msg.sender);
              spokeTreasury[tokenAddress].totalReclaimed += amount;
              IERC20(tokenAddress).safeTransfer(msg.sender, amount);
              IElasticTreasuryHub(msg.sender).treasuryReceiveReclaimedTokens(tokenAddress,amount);
              emit SpokeReclaimTokensEvent(tx.origin, msg.sender, tokenAddress, "Spoke sent Reclaim Tokens back to HUB", amount);
              return true;
          }
          // msg.sender must be the HUB contract address here.  For safety, verify that.
          // no officialSmartContract entity is required here, HUB is 100% trusted as caller.
          // because HUB can only be called by TREAS.
          function treasuryReclaimETH(uint256 amount) external  {
              // avoid underflow and overflow
              require(spokeTreasuryETH.totalReceived >= spokeTreasuryETH.totalReclaimed + amount, "Low balance to reclaim on");
              require(hubTokenAddress == msg.sender);
              spokeTreasuryETH.totalReclaimed += amount;
              IElasticTreasuryHub(payable(msg.sender)).treasuryReceiveReclaimedETH{value : amount}(); // ⇐MAKES TRANSFER
              emit SpokeReclaimEthEvent(tx.origin, msg.sender, "Spoke sent Reclaim ETH back to HUB", amount);
          }
      }// SPDX-License-Identifier: Unlicensed
      // Copyright 2025 US Fintech LLC and Del Norte Holdings.
      // 
      // Permission to use, copy, modify, or distribute this software is strictly prohibited
      // without prior written consent from either copyright holders.
      // 
      // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
      // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
      // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
      // CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE,
      // ARISING FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
      // OFFICIAL DEL NORTE NETWORK COMPONENT
      // PeerTreasury is a contract that allows for the transfer of ETH and ERC20 tokens between different contracts.
      // Designed by Ken Silverman as part of his ElasticTreasury (HUB and SPOKE), PeerTreasury and Controller model.  
      // This deployment is for Trueviewchain Inc. a Panama entity and Del Norte El Salvador S.A  a subsidiary of Del Norte Holdings, Delaware USA.  
      // Permission to change metadata stored on blockchain explorers and elsewhere granted to Del Norte Holdings, DE only.
      // Compilation help from Maleeha Naveed. Deployed by Maleeha Naveed on behalf of Del Norte. May 5th, 2025
      pragma solidity 0.8.29;
      import "../interfaces/IController.sol";
      import "../interfaces/IElasticTreasuryHub.sol";
      import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
      import "./ElasticTreasurySpoke.sol";
      import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
      // Compiled by Maleeha Naveed. May 5th, 2025
      // PeerTreasury is a contract that allows for the transfer of ETH and ERC20 tokens between different contracts.
      /// @author Ken Silverman
      contract PeerTreasury {
          using SafeERC20 for IERC20;
          event PeerTreasuryReceivedETH(address indexed sender, uint256 amount);
          event PeerTreasuryReceivedTokens(address indexed sender, address indexed token, uint256 amount);
          event PeerTreasuryTransferredETH(address indexed to, uint256 amount);
          event PeerTreasuryTransferredTokens(address indexed to, address indexed token, uint256 amount);
          struct TreasuryEntry {
              uint256 totalReceived;
              uint256 totalWithdrawn;
          }
          address public immutable peerTreasuryController;
          mapping(address => TreasuryEntry) private tokenPeerTreasury;
          address[] internal knownTokens; // <-- Needed for enumeration
          TreasuryEntry private ethPeerTreasury;
          modifier onlyTreasuryAdmin() {
              require(
                  IController(peerTreasuryController).isOfficialDoubleEntity("TreasuryAdmin", msg.sender, "SmartContract", msg.sender, false),
                  "Unauthorized"
              );
              _;
          }
          constructor(address _controller) {
              peerTreasuryController = _controller;
          }
          /// @notice Receive ETH from any source and track it
          function peerTreasuryReceiveETH() virtual external payable {
              require(msg.value > 0, "No ETH sent");
              require(IController(peerTreasuryController).isOfficialEntity("SmartContract", msg.sender), "Sender is not an official SmartContract");
              ethPeerTreasury.totalReceived += msg.value;
              emit PeerTreasuryReceivedETH(msg.sender, msg.value);
          }
          /// @notice Transfer ETH to another peer contract (OfficialEntity SmartContract)
          function peerTreasuryTransferETH(address payable to, uint256 amount) virtual public onlyTreasuryAdmin {
              require(address(this).balance >= amount, "Insufficient contract ETH balance");
              require(IController(peerTreasuryController).isOfficialEntity("SmartContract", to), "Recipient not an official SmartContract");
              ethPeerTreasury.totalWithdrawn += amount;
              PeerTreasury(to).peerTreasuryReceiveETH{value: amount}();
              emit PeerTreasuryTransferredETH(to, amount);
          }
          /// @notice Receive ERC20 tokens and track source
          function peerTreasuryReceiveTokens(address token, uint256 amount) virtual external {
              require(amount > 0, "Zero amount");
              require(IController(peerTreasuryController).isOfficialEntity("SmartContract", msg.sender), "Sender not official SC");
              IERC20(token).safeTransferFrom(msg.sender, address(this), amount);
              // Track the token if it's the first time we're seeing it
              if (tokenPeerTreasury[token].totalReceived == 0 && tokenPeerTreasury[token].totalWithdrawn == 0) {
                  knownTokens.push(token);
              }
              tokenPeerTreasury[token].totalReceived += amount;
              emit PeerTreasuryReceivedTokens(msg.sender, token, amount);
          }
          /// @notice Transfer ERC20 tokens to another peer contract (OfficialEntity SmartContract)
          function peerTreasuryTransferTokens(address token, address to, uint256 amount) virtual public onlyTreasuryAdmin {
              require(IERC20(token).balanceOf(address(this)) >= amount, "Insuffic token bal");
              require(IController(peerTreasuryController).isOfficialEntity("SmartContract", to), "recipient not official SC");
              tokenPeerTreasury[token].totalWithdrawn += amount;
              require(IERC20(token).approve(to, amount), "Approve failed");
              PeerTreasury(to).peerTreasuryReceiveTokens(token, amount);
              emit PeerTreasuryTransferredTokens(to, token, amount);
          }
          /// @notice Check ETH available (total received - total withdrawn)
          /// @return The signed balance (can be negative if more was withdrawn than received - which is OK!)
          /// obviously the first lateral movement will be a withdrawal from a peerTreasury source
          function peerETHAvailable() external view returns (int256) {
              // Convert to int256 before subtraction to allow negative results
              return int256(ethPeerTreasury.totalReceived) - int256(ethPeerTreasury.totalWithdrawn);
          }
          /// @notice Check token available (total received - total withdrawn)
          /// @return The signed balance (can be negative if more was withdrawn than received)
          /// obviously the first lateral movement will be a withdrawal from a peerTreasury source
          function peerTokenAvailable(address token) external view returns (int256) {
              TreasuryEntry storage e = tokenPeerTreasury[token];
              // Convert to int256 before subtraction to allow negative results
              return int256(e.totalReceived) - int256(e.totalWithdrawn);
          }
          struct TreasurySnapshot {
              address token;
              uint256 totalReceived;
              uint256 totalWithdrawn;
          }
          /// @notice Return all known peer treasury token entries
         function getPeerTreasuries() external view returns (TreasurySnapshot[] memory) {
              uint256 len = knownTokens.length;
              TreasurySnapshot[] memory result = new TreasurySnapshot[](len);
              for (uint256 i = 0; i < len; i++) {
                  address token = knownTokens[i];
                  TreasuryEntry storage entry = tokenPeerTreasury[token];
                  result[i] = TreasurySnapshot({
                      token: token,
                      totalReceived: entry.totalReceived,
                      totalWithdrawn: entry.totalWithdrawn
                  });
              }
              return result;
          }
          /// @notice Return individual peer treasury entry for a token
          function getPeerTreasury(address token) external view returns (uint256 received, uint256 withdrawn) {
              TreasuryEntry storage entry = tokenPeerTreasury[token];
              return (entry.totalReceived, entry.totalWithdrawn);
          }
          /// @notice Return ETH treasury data
          function getPeerTreasuryETH() external view returns (uint256 received, uint256 withdrawn) {
              return (ethPeerTreasury.totalReceived, ethPeerTreasury.totalWithdrawn);
          }
      }// SPDX-License-Identifier: CLOSED LICENSE COPYRIGHT 2025
      // OFFICAL DEL NORTE NETWORK COMPONENT
      // Designed By Ken Silverman for Del Norte.  Implementation help from Tony Sparks
      // Compilation help from Maleeha Naveed. May 5th, 2025
      pragma solidity 0.8.29;
      import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
      import "../interfaces/IController.sol";
      import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
      /// @title ReversibleRecoveryBase
      /// @notice Base contract tracking / reversing accidental ETH/ERC20 transfers with admin authorization
      abstract contract Recoverable {
          using SafeERC20 for IERC20;
          // STRUCTS
          struct ReversibleUserBalance {
              uint256 totalReceivedThatIsReversible;
              uint256 totalReversed;
              uint256 totalReversals;
          }
          // -----------------------------
          // STORAGE
          // -----------------------------
          address public controller;  // Address of the Controller contract (must implement IController)
          uint256 public ADMIN_FEE_FIXED = 10 ** 17; // 0.1 ETH
          uint256 public totalAdminFeesCollected;
          mapping(address => ReversibleUserBalance) public reversibleEthBalances;
          mapping(address => mapping(address => ReversibleUserBalance)) public reversibleTokenBalances;
          // -----------------------------
          // EVENTS
          // -----------------------------
          event TransferReversed(address indexed user, uint256 refundAmount, address tokenSC, uint256 adminFee);
          event AdminFeeUpdated(uint256 newFee);
          event ControllerChanged(address newController);
           modifier onlyBTExecutives() {
              bool temp = IController(controller).isOfficialEntity("TreasuryAdmin", msg.sender) ||
                          IController(controller).isOfficialEntity("SmartContract", msg.sender);
              require(temp, "Unauthorized access");
              _;  // run the code block referencing this modifier
          }
          // -----------------------------
          // CONSTRUCTOR
          // -----------------------------
          constructor(address _controller) {
              require(_controller != address(0), "Controller address cannot be zero");
              controller = _controller;
          }
          // -----------------------------
          // EXTERNAL METHODS
          // -----------------------------
          function manualUpdateReversibleBalanceETH(address userAddress, uint256 amount)
              external onlyBTExecutives  {
              reversibleEthBalances[userAddress].totalReceivedThatIsReversible += amount;
          }
          function manualUpdateReversibleBalanceERC20(address userAddress, uint256 amount, address tokenSC)
              external onlyBTExecutives {
              reversibleTokenBalances[tokenSC][userAddress].totalReceivedThatIsReversible += amount;
          }
          function reverseAccidentalETH() external payable  {
              require(msg.value >= ADMIN_FEE_FIXED, "Insufficient admin fee");
              require(!IController(controller).isOfficialEntity("Registrar", msg.sender),
                  "Registrars/launchpads may not be allowed to reverse any amounts they send.");
              ReversibleUserBalance storage balance = reversibleEthBalances[msg.sender];
              uint256 refundAmount = balance.totalReceivedThatIsReversible - balance.totalReversed;
              require(refundAmount > 0, "Nothing to refund");
              // Update state before external call
              balance.totalReversed += refundAmount;
              balance.totalReversals += 1;
              totalAdminFeesCollected += msg.value;
              // Perform the external call
              (bool success, ) = msg.sender.call{value: refundAmount}("");
              require(success, "Ether transfer failed");
              emit TransferReversed(msg.sender, refundAmount, address(0), msg.value);
          }
          function reverseAccidentalERC20(address tokenSC) external payable {
              require(msg.value >= ADMIN_FEE_FIXED, "Insufficient admin fee");
              require(!IController(controller).isOfficialEntity("Registrar", msg.sender), "Registrars/launchpads may not reverse any amounts they send.");
              ReversibleUserBalance storage balance = reversibleTokenBalances[tokenSC][msg.sender];
              uint256 refundAmount = balance.totalReceivedThatIsReversible - balance.totalReversed;
              require(refundAmount > 0, "Nothing to refund");
              // Update state before external call
              balance.totalReversed += refundAmount;
              balance.totalReversals += 1;
              totalAdminFeesCollected += msg.value;
              // Perform the external call
              IERC20(tokenSC).safeTransfer(msg.sender, refundAmount);
              emit TransferReversed(msg.sender, refundAmount, tokenSC, msg.value);
        }
          function changeAdminFee(uint256 newFee) external onlyBTExecutives {
              ADMIN_FEE_FIXED = newFee;
              emit AdminFeeUpdated(newFee);
          }
         function changeController(address remote) internal {
              controller = remote;
              emit ControllerChanged(remote);
         }
      }// SPDX-License-Identifier: UNLICENSED
      // Copyright 2025 US Fintech LLC and DelNorte Holdings.
      // 
      // Permission to use, copy, modify, or distribute this software is strictly prohibited
      // without prior written consent from both copyright holders.
      // 
      // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
      // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
      // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
      // CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE,
      // ARISING FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
      // OFFICIAL DEL NORTE NETWORK COMPONENT 
      // Provides immediate membership access to platform at different levels. 
      // Required Non US or accredited US registration to swap for DTV token. Registration available within 180 days per terms.delnorte.io . 
      // Minimally tesed Conroller Tree for world-wide government administration of, well, anything, including property ownership.
      // Designed by Ken Silverman as part of his ElasticTreasury (HUB and SPOKE), PeerTreasury and Controller model. 
      // @author Ken Silverman
      // This deployment is for Del Norte Holdings, Delaware and US Fintech, LLC NY.  
      // Permission to change metadata stored on blockchain explorers and elsewhere granted to:
      // Del Norte Holdings, DE only and/or US Fintech, LLC NY independently
      pragma solidity 0.8.29;
      interface IController {
          struct OfficialEntityStruct {
              string fullNameOfEntityOrLabel;
              string nationalIdOfEntity;
              address pubAddress;
              uint256 blockNumber;
              uint256 blockTimestamp;
              bool active;
          }
          function addOfficialEntity(string memory, address, string memory, string memory) external returns (bool);
          function removeOfficialEntity(string memory, address) external returns (bool);
          function isOfficialEntity(string memory, address) external view returns (bool);
          function isOfficialEntityFast(bytes32, address) external view returns (bool);
          function isOfficialDoubleEntity(string calldata, address, string calldata, address, bool) external view returns (bool);
          function isOfficialDoubleEntityFast(bytes32, address, bytes32, address, bool) external view returns (bool);
          function isOfficialTripleEntityFast(bytes32, address, bytes32, address, bytes32, address, bool) external view returns (bool);
          function isOfficialTripleEntity(string calldata, address, string calldata, address, string calldata, address, bool) external view returns (bool);
          function isOfficialQuadrupleEntityFast(bytes32, address,  bytes32, address, bytes32, address, bytes32, address, bool) external view returns (bool);
          function getOfficialEntity(string calldata, address) external view returns (OfficialEntityStruct memory);
          function getAllOfficialEntities(string calldata) external view returns (OfficialEntityStruct[] memory);
          function init(address, string calldata) external;
      }// SPDX-License-Identifier: MIT
      pragma solidity 0.8.29;
      interface IElasticTreasuryHub {
         function withdrawETHToPerson(address personAddress, uint256 amt, string calldata note) external;
         function withdrawTokensToPerson(address personAddress, uint256 amt, address tokenSCAddress, string calldata note) external;
         function treasuryTransfer(address SCAddress, string calldata allCoinLabel, address tokenAddress, string calldata tokenLabel, uint256 amount) external returns (bool);
         function treasuryTransferETH(address payable SCAddress, string calldata allCoinLabel, string calldata ethLabel, uint256 amount) external returns (bool);
         function treasuryReceiveReclaimedETH() external payable returns (bool);
         function treasuryReceiveReclaimedTokens(address tokenAddress, uint256 amt) external returns (bool);
         function treasuryReclaimRequest(address SCAddress, address tokenAddress, uint256 amount) external returns (bool);
         function treasuryReclaimRequestETH(address payable SCAddress, uint256 amount) external returns (bool);
      }// SPDX-License-Identifier: Copyright 2025
      // OFFICIAL DEL NORTE NETWORK COMPONENT
      // Designed and coded by: Ken Silverman
      //   implementation help by Tony Sparks
      pragma solidity 0.8.29;
      interface IElasticTreasurySpoke {
          function treasuryReceive(address tokenAddress, uint256 amount) external;
          function treasuryReceiveETH() external payable;
          function treasuryReclaim(address tokenAddress, uint256 amount) external returns (bool);
          function treasuryReclaimETH(uint256 amount) external;
      }