false
true
0

Contract Address Details

0x142e6d1020b85BC10bD60e4382120508A81B51C1

Contract Name
BatchCall
Creator
0x7a9066–4cc6eb at 0xae6cd9–b4912f
Balance
0 tPLS
Tokens
Fetching tokens...
Transactions
Fetching transactions...
Transfers
Fetching transfers...
Gas Used
Fetching gas used...
Last Balance Update
25393876
Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
Contract name:
BatchCall




Optimization enabled
true
Compiler version
v0.8.23+commit.f704f362




Optimization runs
999999
EVM Version
paris




Verified at
2024-04-16T09:52:13.563151Z

contracts/BatchCall.sol

// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;

import { ERC20 } from "solady/src/tokens/ERC20.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "@airswap/swap/contracts/interfaces/ISwap.sol";
import "@airswap/swap-erc20/contracts/interfaces/ISwapERC20.sol";
import "@airswap/registry/contracts/interfaces/IRegistry.sol";

/**
 * @title BatchCall: Batch balance, allowance, order validity checks, nonce usage check
 */
contract BatchCall {
  using Address for address;

  error ArgumentInvalid();

  /**
   * @notice Check the token balance of a wallet in a token contract
   * @dev return 0 on returns 0 on invalid spender contract or non-contract address
   * @param userAddress address
   * @param tokenAddress address
   * @return uint256 token balance if possible
   */
  function tokenBalance(
    address userAddress,
    address tokenAddress
  ) public view returns (uint256) {
    if (tokenAddress.isContract()) {
      ERC20 token = ERC20(tokenAddress);
      //  Check if balanceOf succeeds.
      (bool success, ) = address(token).staticcall(
        abi.encodeWithSelector(token.balanceOf.selector, userAddress)
      );
      if (success) {
        return token.balanceOf(userAddress);
      }
      return 0;
    }
    return 0;
  }

  /**
   * @notice Check the token balances of a wallet for multiple tokens
   * @dev return array and will fail if large token arrays are inputted
   * @dev Returns array of token balances in base units
   * @param userAddress address
   * @param tokenAddresses address[]
   * @return uint256[] token balance array if possible
   */
  function walletBalances(
    address userAddress,
    address[] calldata tokenAddresses
  ) external view returns (uint256[] memory) {
    if (tokenAddresses.length <= 0) revert ArgumentInvalid();
    uint256[] memory balances = new uint256[](tokenAddresses.length);

    for (uint256 i; i < tokenAddresses.length; ) {
      if (tokenAddresses[i] != address(0)) {
        balances[i] = tokenBalance(userAddress, tokenAddresses[i]);
      } else {
        balances[i] = userAddress.balance;
      }
      unchecked {
        ++i;
      }
    }
    return balances;
  }

  /**
   * @notice Check the token balances of multiple wallets for multiple tokens
   * @dev return array and will fail if large arrays are inputted
   * @dev Returns array of token balances in base units
   * @param userAddresses address[]
   * @param tokenAddresses address[]
   * @return uint256[] token allowances array if possible
   */
  function allBalancesForManyAccounts(
    address[] calldata userAddresses,
    address[] calldata tokenAddresses
  ) external view returns (uint256[] memory) {
    uint256[] memory balances = new uint256[](
      tokenAddresses.length * userAddresses.length
    );
    for (uint256 i; i < userAddresses.length; ) {
      for (uint256 j; j < tokenAddresses.length; ) {
        if (tokenAddresses[j] != address(0)) {
          balances[(i * tokenAddresses.length) + j] = tokenBalance(
            userAddresses[i],
            tokenAddresses[j]
          );
        } else {
          balances[(i * tokenAddresses.length) + j] = userAddresses[i].balance;
        }
        unchecked {
          ++j;
        }
      }
      unchecked {
        ++i;
      }
    }
    return balances;
  }

  /**
   * @notice Check the token allowance of a wallet in a token contract
   * @dev return 0 on returns 0 on invalid spender contract or non-contract address
   * @param userAddress address
   * @param spenderAddress address Specified address to spend
   * @param tokenAddress address
   * @return uint256 token allowance if possible
   */
  function tokenAllowance(
    address userAddress,
    address spenderAddress,
    address tokenAddress
  ) public view returns (uint256) {
    if (tokenAddress.isContract()) {
      ERC20 token = ERC20(tokenAddress);
      // Check if allowance succeeds as a call else returns 0.
      (bool success, ) = address(token).staticcall(
        abi.encodeWithSelector(
          token.allowance.selector,
          userAddress,
          spenderAddress
        )
      );
      if (success) {
        return token.allowance(userAddress, spenderAddress);
      }
      return 0;
    }
    return 0;
  }

  /**
   * @notice Check the token allowances of a wallet for multiple tokens
   * @dev return array and will fail if large token arrays are inputted
   * @dev Returns array of token allowances in base units
   * @param userAddress address
   * @param spenderAddress address
   * @param tokenAddresses address[]
   * @return uint256[] token allowances array if possible
   */
  function walletAllowances(
    address userAddress,
    address spenderAddress,
    address[] calldata tokenAddresses
  ) external view returns (uint256[] memory) {
    if (tokenAddresses.length <= 0) revert ArgumentInvalid();
    uint256[] memory allowances = new uint256[](tokenAddresses.length);

    for (uint256 i; i < tokenAddresses.length; ) {
      allowances[i] = tokenAllowance(
        userAddress,
        spenderAddress,
        tokenAddresses[i]
      );
      unchecked {
        ++i;
      }
    }
    return allowances;
  }

  /**
   * @notice Check the token allowances of multiple wallets for multiple tokens
   * @dev return array and will fail if large arrays are inputted
   * @dev Returns array of token allowances in base units
   * @param userAddresses address[]
   * @param spenderAddress address
   * @param tokenAddresses address[]
   * @return uint256[] token allowances array if possible
   */
  function allAllowancesForManyAccounts(
    address[] calldata userAddresses,
    address spenderAddress,
    address[] calldata tokenAddresses
  ) external view returns (uint256[] memory) {
    uint256[] memory allowances = new uint256[](
      tokenAddresses.length * userAddresses.length
    );

    for (uint256 i; i < userAddresses.length; ) {
      for (uint256 j; j < tokenAddresses.length; ) {
        allowances[(i * tokenAddresses.length) + j] = tokenAllowance(
          userAddresses[i],
          spenderAddress,
          tokenAddresses[j]
        );
        unchecked {
          ++j;
        }
      }
      unchecked {
        ++i;
      }
    }
    return allowances;
  }

  /**
   * @notice Check validity of an array of Orders
   * @param senderWallet address Wallet that would send the order
   * @param orders ISwap.Order[] Array of orders to be checked
   * @param swapContract ISwap Swap contract to call
   * @return bool[] True indicates the order is valid
   */
  function getOrdersValid(
    address senderWallet,
    ISwap.Order[] calldata orders,
    ISwap swapContract
  ) external view returns (bool[] memory) {
    if (orders.length <= 0) revert ArgumentInvalid();
    bool[] memory orderValidity = new bool[](orders.length);

    for (uint256 i; i < orders.length; ) {
      bytes32[] memory errors = swapContract.check(senderWallet, orders[i]);
      orderValidity[i] = errors.length == 0 ? true : false;
      unchecked {
        ++i;
      }
    }
    return orderValidity;
  }

  /**
   * @notice Check validity of an array of OrderERC20s
   * @param senderWallet address Wallet that would send the order
   * @param orders ISwapERC20.OrderERC20[] Array of orders to be checked
   * @param swapERC20Contract ISwapERC20 Swap contract to call
   * @return bool[] True indicates the order is valid
   */
  function getOrdersValidERC20(
    address senderWallet,
    ISwapERC20.OrderERC20[] calldata orders,
    ISwapERC20 swapERC20Contract
  ) external view returns (bool[] memory) {
    if (orders.length <= 0) revert ArgumentInvalid();
    bool[] memory orderValidity = new bool[](orders.length);

    for (uint256 i; i < orders.length; ) {
      ISwapERC20.OrderERC20 memory order = orders[i];
      bytes32[] memory errors = swapERC20Contract.check(
        senderWallet,
        order.nonce,
        order.expiry,
        order.signerWallet,
        order.signerToken,
        order.signerAmount,
        order.senderToken,
        order.senderAmount,
        order.v,
        order.r,
        order.s
      );
      orderValidity[i] = errors.length == 0 ? true : false;
      unchecked {
        ++i;
      }
    }
    return orderValidity;
  }

  /**
   * @notice Checks usage of an array of nonces
   * @dev Swap and SwapERC20 nonceUsed function have the same signature
   * @param signerWallets address[] list of signers for each nonce
   * @param nonces uint256[] list of nonces to be checked
   * @param swapContract ISwap[] Swap or SwapERC20 contract to call
   * @return bool[] true indicates the nonce is used
   */
  function getNoncesUsed(
    address[] calldata signerWallets,
    uint256[] calldata nonces,
    ISwap swapContract
  ) external view returns (bool[] memory) {
    if (signerWallets.length == 0) revert ArgumentInvalid();
    if (signerWallets.length != nonces.length) revert ArgumentInvalid();
    bool[] memory nonceUsed = new bool[](signerWallets.length);

    for (uint256 i; i < signerWallets.length; ) {
      nonceUsed[i] = swapContract.nonceUsed(signerWallets[i], nonces[i]);
      unchecked {
        ++i;
      }
    }
    return nonceUsed;
  }

  /**
   * @notice provides the tokens supported by multiple Stakers
   * @param stakers address[] list of stakers to be checked
   * @param registryContract IRegistry Registry contract to call
   * @return bool[] true indicates the nonce is used
   */
  function getTokensForStakers(
    address[] calldata stakers,
    IRegistry registryContract
  ) external view returns (address[][] memory) {
    if (stakers.length == 0) revert ArgumentInvalid();
    address[][] memory tokensSupported = new address[][](stakers.length);

    for (uint256 i; i < stakers.length; ) {
      tokensSupported[i] = registryContract.getTokensForStaker(stakers[i]);
      unchecked {
        ++i;
      }
    }
    return tokensSupported;
  }
}
        

@airswap/registry/contracts/interfaces/IRegistry.sol

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.23;

interface IRegistry {
  event SetServerURL(address indexed staker, string url);
  event AddProtocols(address indexed staker, bytes4[] protocols);
  event AddTokens(address indexed staker, address[] tokens);
  event RemoveProtocols(address indexed staker, bytes4[] protocols);
  event RemoveTokens(address indexed staker, address[] tokens);
  event UnsetServer(
    address indexed staker,
    string url,
    bytes4[] protocols,
    address[] tokens
  );

  error ArgumentInvalid();
  error NoServerURLSet();
  error ProtocolDoesNotExist(bytes4);
  error ProtocolExists(bytes4);
  error TokenDoesNotExist(address);
  error TokenExists(address);
  error ServerURLInvalid();

  function setServerURL(string calldata _url) external;

  function unsetServer() external;

  function addProtocols(bytes4[] calldata _protocols) external;

  function removeProtocols(bytes4[] calldata _protocols) external;

  function getServerURLsForProtocol(
    bytes4 _protocol
  ) external view returns (string[] memory _urls);

  function supportsProtocol(
    address _staker,
    bytes4 _protocol
  ) external view returns (bool);

  function getProtocolsForStaker(
    address _staker
  ) external view returns (bytes4[] memory _protocolList);

  function getStakersForProtocol(
    bytes4 _protocol
  ) external view returns (address[] memory _stakers);

  function addTokens(address[] calldata _tokens) external;

  function removeTokens(address[] calldata _tokens) external;

  function getServerURLsForToken(
    address _token
  ) external view returns (string[] memory urls);

  function supportsToken(
    address _staker,
    address _token
  ) external view returns (bool);

  function getTokensForStaker(
    address _staker
  ) external view returns (address[] memory tokenList);

  function getStakersForToken(
    address _token
  ) external view returns (address[] memory _stakers);

  function getServerURLsForStakers(
    address[] calldata _stakers
  ) external view returns (string[] memory _urls);

  function balanceOf(address _staker) external view returns (uint256);
}
          

@airswap/swap-erc20/contracts/interfaces/ISwapERC20.sol

// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;

interface ISwapERC20 {
  struct OrderERC20 {
    uint256 nonce; // Unique number per signatory per order
    uint256 expiry; // Expiry time (seconds since unix epoch)
    address signerWallet; // Party to the swap that sets terms
    address signerToken; // ERC20 token address transferred from signer
    uint256 signerAmount; // Amount of tokens transferred from signer
    address senderWallet; // Party to the swap that accepts terms
    address senderToken; // ERC20 token address transferred from sender
    uint256 senderAmount; // Amount of tokens transferred from sender
    uint8 v; // ECDSA
    bytes32 r;
    bytes32 s;
  }

  event SwapERC20(uint256 indexed nonce, address indexed signerWallet);

  event Cancel(uint256 indexed nonce, address indexed signerWallet);
  event Authorize(address indexed signer, address indexed signerWallet);
  event Revoke(address indexed signer, address indexed signerWallet);
  event SetProtocolFee(uint256 protocolFee);
  event SetProtocolFeeLight(uint256 protocolFeeLight);
  event SetProtocolFeeWallet(address indexed feeWallet);
  event SetBonusScale(uint256 bonusScale);
  event SetBonusMax(uint256 bonusMax);
  event SetStaking(address indexed staking);

  error ChainIdChanged();
  error InvalidFee();
  error InvalidFeeLight();
  error InvalidFeeWallet();
  error InvalidStaking();
  error OrderExpired();
  error MaxTooHigh();
  error NonceAlreadyUsed(uint256);
  error ScaleTooHigh();
  error SignatoryInvalid();
  error SignatureInvalid();
  error TransferFromFailed();

  function swap(
    address recipient,
    uint256 nonce,
    uint256 expiry,
    address signerWallet,
    address signerToken,
    uint256 signerAmount,
    address senderToken,
    uint256 senderAmount,
    uint8 v,
    bytes32 r,
    bytes32 s
  ) external;

  function swapAnySender(
    address recipient,
    uint256 nonce,
    uint256 expiry,
    address signerWallet,
    address signerToken,
    uint256 signerAmount,
    address senderToken,
    uint256 senderAmount,
    uint8 v,
    bytes32 r,
    bytes32 s
  ) external;

  function swapLight(
    uint256 nonce,
    uint256 expiry,
    address signerWallet,
    address signerToken,
    uint256 signerAmount,
    address senderToken,
    uint256 senderAmount,
    uint8 v,
    bytes32 r,
    bytes32 s
  ) external;

  function authorize(address sender) external;

  function revoke() external;

  function cancel(uint256[] calldata nonces) external;

  function check(
    address senderWallet,
    uint256 nonce,
    uint256 expiry,
    address signerWallet,
    address signerToken,
    uint256 signerAmount,
    address senderToken,
    uint256 senderAmount,
    uint8 v,
    bytes32 r,
    bytes32 s
  ) external view returns (bytes32[] memory);

  function nonceUsed(address, uint256) external view returns (bool);

  function authorized(address) external view returns (address);

  function calculateProtocolFee(
    address,
    uint256
  ) external view returns (uint256);
}
          

@airswap/swap/contracts/interfaces/IAdapter.sol

// SPDX-License-Identifier: MIT

pragma solidity 0.8.23;

struct Party {
  address wallet; // Wallet address of the party
  address token; // Contract address of the token
  bytes4 kind; // Interface ID of the token
  uint256 id; // ID for ERC-721 or ERC-1155
  uint256 amount; // Amount for ERC-20 or ERC-1155
}

/**
 * @title IAdapter: Adapter for various token kinds
 */
interface IAdapter {
  /**
   * @notice Revert if provided an invalid transfer argument
   */
  error AmountOrIDInvalid(string);

  /**
   * @notice Return the ERC165 interfaceId this adapter supports
   */
  function interfaceId() external view returns (bytes4);

  /**
   * @notice Checks allowance on a token
   * @param party Party params to check
   */
  function hasAllowance(Party calldata party) external view returns (bool);

  /**
   * @notice Checks balance on a token
   * @param party Party params to check
   */
  function hasBalance(Party calldata party) external view returns (bool);

  /**
   * @notice Checks params for transfer
   * @param party Party params to check
   */
  function hasValidParams(Party calldata party) external view returns (bool);

  /**
   * @notice Function to wrap token transfer for different token types
   * @param from address Wallet address to transfer from
   * @param to address Wallet address to transfer to
   * @param amount uint256 Amount for ERC-20
   * @param id token ID for ERC-721
   * @param token address Contract address of token
   */
  function transfer(
    address from,
    address to,
    uint256 amount,
    uint256 id,
    address token
  ) external;
}
          

@airswap/swap/contracts/interfaces/ISwap.sol

// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;

import "./IAdapter.sol";

interface ISwap {
  struct Order {
    uint256 nonce; // Unique number per signatory per order
    uint256 expiry; // Expiry time (seconds since unix epoch)
    Party signer; // Party to the swap that sets terms
    Party sender; // Party to the swap that accepts terms
    address affiliateWallet; // Party tipped for facilitating (optional)
    uint256 affiliateAmount;
    uint8 v; // ECDSA
    bytes32 r;
    bytes32 s;
  }

  event Swap(
    uint256 indexed nonce,
    address indexed signerWallet,
    uint256 signerAmount,
    uint256 signerId,
    address signerToken,
    address indexed senderWallet,
    uint256 senderAmount,
    uint256 senderId,
    address senderToken,
    address affiliateWallet,
    uint256 affiliateAmount
  );
  event Cancel(uint256 indexed nonce, address indexed signerWallet);
  event CancelUpTo(uint256 indexed nonce, address indexed signerWallet);
  event SetProtocolFee(uint256 protocolFee);
  event SetProtocolFeeWallet(address indexed feeWallet);
  event Authorize(address indexed signer, address indexed signerWallet);
  event Revoke(address indexed signer, address indexed signerWallet);

  error ChainIdChanged();
  error AdaptersInvalid();
  error FeeInvalid();
  error FeeWalletInvalid();
  error NonceAlreadyUsed(uint256);
  error NonceTooLow();
  error OrderExpired();
  error SenderInvalid();
  error SenderTokenInvalid();
  error AffiliateAmountInvalid();
  error SignatureInvalid();
  error SignatoryInvalid();
  error RoyaltyExceedsMax(uint256);
  error TokenKindUnknown();
  error TransferFailed(address, address);
  error SignatoryUnauthorized();
  error Unauthorized();

  function swap(
    address recipient,
    uint256 maxRoyalty,
    Order calldata order
  ) external;

  function cancel(uint256[] calldata nonces) external;

  function cancelUpTo(uint256 minimumNonce) external;

  function check(
    address,
    Order calldata
  ) external view returns (bytes32[] memory);

  function nonceUsed(address, uint256) external view returns (bool);

  function authorize(address sender) external;

  function revoke() external;

  function adapters(bytes4) external view returns (IAdapter);

  function authorized(address) external view returns (address);

  function signatoryMinimumNonce(address) external view returns (uint256);
}
          

@openzeppelin/contracts/utils/Address.sol

// 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);
        }
    }
}
          

solady/src/tokens/ERC20.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Simple ERC20 + EIP-2612 implementation.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol)
///
/// @dev Note:
/// - The ERC20 standard allows minting and transferring to and from the zero address,
///   minting and transferring zero tokens, as well as self-approvals.
///   For performance, this implementation WILL NOT revert for such actions.
///   Please add any checks with overrides if desired.
/// - The `permit` function uses the ecrecover precompile (0x1).
///
/// If you are overriding:
/// - NEVER violate the ERC20 invariant:
///   the total sum of all balances must be equal to `totalSupply()`.
/// - Check that the overridden function is actually used in the function you want to
///   change the behavior of. Much of the code has been manually inlined for performance.
abstract contract ERC20 {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The total supply has overflowed.
    error TotalSupplyOverflow();

    /// @dev The allowance has overflowed.
    error AllowanceOverflow();

    /// @dev The allowance has underflowed.
    error AllowanceUnderflow();

    /// @dev Insufficient balance.
    error InsufficientBalance();

    /// @dev Insufficient allowance.
    error InsufficientAllowance();

    /// @dev The permit is invalid.
    error InvalidPermit();

    /// @dev The permit has expired.
    error PermitExpired();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                           EVENTS                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Emitted when `amount` tokens is transferred from `from` to `to`.
    event Transfer(address indexed from, address indexed to, uint256 amount);

    /// @dev Emitted when `amount` tokens is approved by `owner` to be used by `spender`.
    event Approval(address indexed owner, address indexed spender, uint256 amount);

    /// @dev `keccak256(bytes("Transfer(address,address,uint256)"))`.
    uint256 private constant _TRANSFER_EVENT_SIGNATURE =
        0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef;

    /// @dev `keccak256(bytes("Approval(address,address,uint256)"))`.
    uint256 private constant _APPROVAL_EVENT_SIGNATURE =
        0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                          STORAGE                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The storage slot for the total supply.
    uint256 private constant _TOTAL_SUPPLY_SLOT = 0x05345cdf77eb68f44c;

    /// @dev The balance slot of `owner` is given by:
    /// ```
    ///     mstore(0x0c, _BALANCE_SLOT_SEED)
    ///     mstore(0x00, owner)
    ///     let balanceSlot := keccak256(0x0c, 0x20)
    /// ```
    uint256 private constant _BALANCE_SLOT_SEED = 0x87a211a2;

    /// @dev The allowance slot of (`owner`, `spender`) is given by:
    /// ```
    ///     mstore(0x20, spender)
    ///     mstore(0x0c, _ALLOWANCE_SLOT_SEED)
    ///     mstore(0x00, owner)
    ///     let allowanceSlot := keccak256(0x0c, 0x34)
    /// ```
    uint256 private constant _ALLOWANCE_SLOT_SEED = 0x7f5e9f20;

    /// @dev The nonce slot of `owner` is given by:
    /// ```
    ///     mstore(0x0c, _NONCES_SLOT_SEED)
    ///     mstore(0x00, owner)
    ///     let nonceSlot := keccak256(0x0c, 0x20)
    /// ```
    uint256 private constant _NONCES_SLOT_SEED = 0x38377508;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         CONSTANTS                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev `(_NONCES_SLOT_SEED << 16) | 0x1901`.
    uint256 private constant _NONCES_SLOT_SEED_WITH_SIGNATURE_PREFIX = 0x383775081901;

    /// @dev `keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")`.
    bytes32 private constant _DOMAIN_TYPEHASH =
        0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f;

    /// @dev `keccak256("1")`.
    bytes32 private constant _VERSION_HASH =
        0xc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6;

    /// @dev `keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)")`.
    bytes32 private constant _PERMIT_TYPEHASH =
        0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       ERC20 METADATA                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns the name of the token.
    function name() public view virtual returns (string memory);

    /// @dev Returns the symbol of the token.
    function symbol() public view virtual returns (string memory);

    /// @dev Returns the decimals places of the token.
    function decimals() public view virtual returns (uint8) {
        return 18;
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                           ERC20                            */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns the amount of tokens in existence.
    function totalSupply() public view virtual returns (uint256 result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := sload(_TOTAL_SUPPLY_SLOT)
        }
    }

    /// @dev Returns the amount of tokens owned by `owner`.
    function balanceOf(address owner) public view virtual returns (uint256 result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x0c, _BALANCE_SLOT_SEED)
            mstore(0x00, owner)
            result := sload(keccak256(0x0c, 0x20))
        }
    }

    /// @dev Returns the amount of tokens that `spender` can spend on behalf of `owner`.
    function allowance(address owner, address spender)
        public
        view
        virtual
        returns (uint256 result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x20, spender)
            mstore(0x0c, _ALLOWANCE_SLOT_SEED)
            mstore(0x00, owner)
            result := sload(keccak256(0x0c, 0x34))
        }
    }

    /// @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
    ///
    /// Emits a {Approval} event.
    function approve(address spender, uint256 amount) public virtual returns (bool) {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute the allowance slot and store the amount.
            mstore(0x20, spender)
            mstore(0x0c, _ALLOWANCE_SLOT_SEED)
            mstore(0x00, caller())
            sstore(keccak256(0x0c, 0x34), amount)
            // Emit the {Approval} event.
            mstore(0x00, amount)
            log3(0x00, 0x20, _APPROVAL_EVENT_SIGNATURE, caller(), shr(96, mload(0x2c)))
        }
        return true;
    }

    /// @dev Transfer `amount` tokens from the caller to `to`.
    ///
    /// Requirements:
    /// - `from` must at least have `amount`.
    ///
    /// Emits a {Transfer} event.
    function transfer(address to, uint256 amount) public virtual returns (bool) {
        _beforeTokenTransfer(msg.sender, to, amount);
        /// @solidity memory-safe-assembly
        assembly {
            // Compute the balance slot and load its value.
            mstore(0x0c, _BALANCE_SLOT_SEED)
            mstore(0x00, caller())
            let fromBalanceSlot := keccak256(0x0c, 0x20)
            let fromBalance := sload(fromBalanceSlot)
            // Revert if insufficient balance.
            if gt(amount, fromBalance) {
                mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
                revert(0x1c, 0x04)
            }
            // Subtract and store the updated balance.
            sstore(fromBalanceSlot, sub(fromBalance, amount))
            // Compute the balance slot of `to`.
            mstore(0x00, to)
            let toBalanceSlot := keccak256(0x0c, 0x20)
            // Add and store the updated balance of `to`.
            // Will not overflow because the sum of all user balances
            // cannot exceed the maximum uint256 value.
            sstore(toBalanceSlot, add(sload(toBalanceSlot), amount))
            // Emit the {Transfer} event.
            mstore(0x20, amount)
            log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, caller(), shr(96, mload(0x0c)))
        }
        _afterTokenTransfer(msg.sender, to, amount);
        return true;
    }

    /// @dev Transfers `amount` tokens from `from` to `to`.
    ///
    /// Note: Does not update the allowance if it is the maximum uint256 value.
    ///
    /// Requirements:
    /// - `from` must at least have `amount`.
    /// - The caller must have at least `amount` of allowance to transfer the tokens of `from`.
    ///
    /// Emits a {Transfer} event.
    function transferFrom(address from, address to, uint256 amount) public virtual returns (bool) {
        _beforeTokenTransfer(from, to, amount);
        /// @solidity memory-safe-assembly
        assembly {
            let from_ := shl(96, from)
            // Compute the allowance slot and load its value.
            mstore(0x20, caller())
            mstore(0x0c, or(from_, _ALLOWANCE_SLOT_SEED))
            let allowanceSlot := keccak256(0x0c, 0x34)
            let allowance_ := sload(allowanceSlot)
            // If the allowance is not the maximum uint256 value.
            if add(allowance_, 1) {
                // Revert if the amount to be transferred exceeds the allowance.
                if gt(amount, allowance_) {
                    mstore(0x00, 0x13be252b) // `InsufficientAllowance()`.
                    revert(0x1c, 0x04)
                }
                // Subtract and store the updated allowance.
                sstore(allowanceSlot, sub(allowance_, amount))
            }
            // Compute the balance slot and load its value.
            mstore(0x0c, or(from_, _BALANCE_SLOT_SEED))
            let fromBalanceSlot := keccak256(0x0c, 0x20)
            let fromBalance := sload(fromBalanceSlot)
            // Revert if insufficient balance.
            if gt(amount, fromBalance) {
                mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
                revert(0x1c, 0x04)
            }
            // Subtract and store the updated balance.
            sstore(fromBalanceSlot, sub(fromBalance, amount))
            // Compute the balance slot of `to`.
            mstore(0x00, to)
            let toBalanceSlot := keccak256(0x0c, 0x20)
            // Add and store the updated balance of `to`.
            // Will not overflow because the sum of all user balances
            // cannot exceed the maximum uint256 value.
            sstore(toBalanceSlot, add(sload(toBalanceSlot), amount))
            // Emit the {Transfer} event.
            mstore(0x20, amount)
            log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, from_), shr(96, mload(0x0c)))
        }
        _afterTokenTransfer(from, to, amount);
        return true;
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                          EIP-2612                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev For more performance, override to return the constant value
    /// of `keccak256(bytes(name()))` if `name()` will never change.
    function _constantNameHash() internal view virtual returns (bytes32 result) {}

    /// @dev Returns the current nonce for `owner`.
    /// This value is used to compute the signature for EIP-2612 permit.
    function nonces(address owner) public view virtual returns (uint256 result) {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute the nonce slot and load its value.
            mstore(0x0c, _NONCES_SLOT_SEED)
            mstore(0x00, owner)
            result := sload(keccak256(0x0c, 0x20))
        }
    }

    /// @dev Sets `value` as the allowance of `spender` over the tokens of `owner`,
    /// authorized by a signed approval by `owner`.
    ///
    /// Emits a {Approval} event.
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) public virtual {
        bytes32 nameHash = _constantNameHash();
        //  We simply calculate it on-the-fly to allow for cases where the `name` may change.
        if (nameHash == bytes32(0)) nameHash = keccak256(bytes(name()));
        /// @solidity memory-safe-assembly
        assembly {
            // Revert if the block timestamp is greater than `deadline`.
            if gt(timestamp(), deadline) {
                mstore(0x00, 0x1a15a3cc) // `PermitExpired()`.
                revert(0x1c, 0x04)
            }
            let m := mload(0x40) // Grab the free memory pointer.
            // Clean the upper 96 bits.
            owner := shr(96, shl(96, owner))
            spender := shr(96, shl(96, spender))
            // Compute the nonce slot and load its value.
            mstore(0x0e, _NONCES_SLOT_SEED_WITH_SIGNATURE_PREFIX)
            mstore(0x00, owner)
            let nonceSlot := keccak256(0x0c, 0x20)
            let nonceValue := sload(nonceSlot)
            // Prepare the domain separator.
            mstore(m, _DOMAIN_TYPEHASH)
            mstore(add(m, 0x20), nameHash)
            mstore(add(m, 0x40), _VERSION_HASH)
            mstore(add(m, 0x60), chainid())
            mstore(add(m, 0x80), address())
            mstore(0x2e, keccak256(m, 0xa0))
            // Prepare the struct hash.
            mstore(m, _PERMIT_TYPEHASH)
            mstore(add(m, 0x20), owner)
            mstore(add(m, 0x40), spender)
            mstore(add(m, 0x60), value)
            mstore(add(m, 0x80), nonceValue)
            mstore(add(m, 0xa0), deadline)
            mstore(0x4e, keccak256(m, 0xc0))
            // Prepare the ecrecover calldata.
            mstore(0x00, keccak256(0x2c, 0x42))
            mstore(0x20, and(0xff, v))
            mstore(0x40, r)
            mstore(0x60, s)
            let t := staticcall(gas(), 1, 0, 0x80, 0x20, 0x20)
            // If the ecrecover fails, the returndatasize will be 0x00,
            // `owner` will be checked if it equals the hash at 0x00,
            // which evaluates to false (i.e. 0), and we will revert.
            // If the ecrecover succeeds, the returndatasize will be 0x20,
            // `owner` will be compared against the returned address at 0x20.
            if iszero(eq(mload(returndatasize()), owner)) {
                mstore(0x00, 0xddafbaef) // `InvalidPermit()`.
                revert(0x1c, 0x04)
            }
            // Increment and store the updated nonce.
            sstore(nonceSlot, add(nonceValue, t)) // `t` is 1 if ecrecover succeeds.
            // Compute the allowance slot and store the value.
            // The `owner` is already at slot 0x20.
            mstore(0x40, or(shl(160, _ALLOWANCE_SLOT_SEED), spender))
            sstore(keccak256(0x2c, 0x34), value)
            // Emit the {Approval} event.
            log3(add(m, 0x60), 0x20, _APPROVAL_EVENT_SIGNATURE, owner, spender)
            mstore(0x40, m) // Restore the free memory pointer.
            mstore(0x60, 0) // Restore the zero pointer.
        }
    }

    /// @dev Returns the EIP-712 domain separator for the EIP-2612 permit.
    function DOMAIN_SEPARATOR() public view virtual returns (bytes32 result) {
        bytes32 nameHash = _constantNameHash();
        //  We simply calculate it on-the-fly to allow for cases where the `name` may change.
        if (nameHash == bytes32(0)) nameHash = keccak256(bytes(name()));
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Grab the free memory pointer.
            mstore(m, _DOMAIN_TYPEHASH)
            mstore(add(m, 0x20), nameHash)
            mstore(add(m, 0x40), _VERSION_HASH)
            mstore(add(m, 0x60), chainid())
            mstore(add(m, 0x80), address())
            result := keccak256(m, 0xa0)
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  INTERNAL MINT FUNCTIONS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Mints `amount` tokens to `to`, increasing the total supply.
    ///
    /// Emits a {Transfer} event.
    function _mint(address to, uint256 amount) internal virtual {
        _beforeTokenTransfer(address(0), to, amount);
        /// @solidity memory-safe-assembly
        assembly {
            let totalSupplyBefore := sload(_TOTAL_SUPPLY_SLOT)
            let totalSupplyAfter := add(totalSupplyBefore, amount)
            // Revert if the total supply overflows.
            if lt(totalSupplyAfter, totalSupplyBefore) {
                mstore(0x00, 0xe5cfe957) // `TotalSupplyOverflow()`.
                revert(0x1c, 0x04)
            }
            // Store the updated total supply.
            sstore(_TOTAL_SUPPLY_SLOT, totalSupplyAfter)
            // Compute the balance slot and load its value.
            mstore(0x0c, _BALANCE_SLOT_SEED)
            mstore(0x00, to)
            let toBalanceSlot := keccak256(0x0c, 0x20)
            // Add and store the updated balance.
            sstore(toBalanceSlot, add(sload(toBalanceSlot), amount))
            // Emit the {Transfer} event.
            mstore(0x20, amount)
            log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, 0, shr(96, mload(0x0c)))
        }
        _afterTokenTransfer(address(0), to, amount);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  INTERNAL BURN FUNCTIONS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Burns `amount` tokens from `from`, reducing the total supply.
    ///
    /// Emits a {Transfer} event.
    function _burn(address from, uint256 amount) internal virtual {
        _beforeTokenTransfer(from, address(0), amount);
        /// @solidity memory-safe-assembly
        assembly {
            // Compute the balance slot and load its value.
            mstore(0x0c, _BALANCE_SLOT_SEED)
            mstore(0x00, from)
            let fromBalanceSlot := keccak256(0x0c, 0x20)
            let fromBalance := sload(fromBalanceSlot)
            // Revert if insufficient balance.
            if gt(amount, fromBalance) {
                mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
                revert(0x1c, 0x04)
            }
            // Subtract and store the updated balance.
            sstore(fromBalanceSlot, sub(fromBalance, amount))
            // Subtract and store the updated total supply.
            sstore(_TOTAL_SUPPLY_SLOT, sub(sload(_TOTAL_SUPPLY_SLOT), amount))
            // Emit the {Transfer} event.
            mstore(0x00, amount)
            log3(0x00, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, shl(96, from)), 0)
        }
        _afterTokenTransfer(from, address(0), amount);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                INTERNAL TRANSFER FUNCTIONS                 */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Moves `amount` of tokens from `from` to `to`.
    function _transfer(address from, address to, uint256 amount) internal virtual {
        _beforeTokenTransfer(from, to, amount);
        /// @solidity memory-safe-assembly
        assembly {
            let from_ := shl(96, from)
            // Compute the balance slot and load its value.
            mstore(0x0c, or(from_, _BALANCE_SLOT_SEED))
            let fromBalanceSlot := keccak256(0x0c, 0x20)
            let fromBalance := sload(fromBalanceSlot)
            // Revert if insufficient balance.
            if gt(amount, fromBalance) {
                mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
                revert(0x1c, 0x04)
            }
            // Subtract and store the updated balance.
            sstore(fromBalanceSlot, sub(fromBalance, amount))
            // Compute the balance slot of `to`.
            mstore(0x00, to)
            let toBalanceSlot := keccak256(0x0c, 0x20)
            // Add and store the updated balance of `to`.
            // Will not overflow because the sum of all user balances
            // cannot exceed the maximum uint256 value.
            sstore(toBalanceSlot, add(sload(toBalanceSlot), amount))
            // Emit the {Transfer} event.
            mstore(0x20, amount)
            log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, from_), shr(96, mload(0x0c)))
        }
        _afterTokenTransfer(from, to, amount);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                INTERNAL ALLOWANCE FUNCTIONS                */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Updates the allowance of `owner` for `spender` based on spent `amount`.
    function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute the allowance slot and load its value.
            mstore(0x20, spender)
            mstore(0x0c, _ALLOWANCE_SLOT_SEED)
            mstore(0x00, owner)
            let allowanceSlot := keccak256(0x0c, 0x34)
            let allowance_ := sload(allowanceSlot)
            // If the allowance is not the maximum uint256 value.
            if add(allowance_, 1) {
                // Revert if the amount to be transferred exceeds the allowance.
                if gt(amount, allowance_) {
                    mstore(0x00, 0x13be252b) // `InsufficientAllowance()`.
                    revert(0x1c, 0x04)
                }
                // Subtract and store the updated allowance.
                sstore(allowanceSlot, sub(allowance_, amount))
            }
        }
    }

    /// @dev Sets `amount` as the allowance of `spender` over the tokens of `owner`.
    ///
    /// Emits a {Approval} event.
    function _approve(address owner, address spender, uint256 amount) internal virtual {
        /// @solidity memory-safe-assembly
        assembly {
            let owner_ := shl(96, owner)
            // Compute the allowance slot and store the amount.
            mstore(0x20, spender)
            mstore(0x0c, or(owner_, _ALLOWANCE_SLOT_SEED))
            sstore(keccak256(0x0c, 0x34), amount)
            // Emit the {Approval} event.
            mstore(0x00, amount)
            log3(0x00, 0x20, _APPROVAL_EVENT_SIGNATURE, shr(96, owner_), shr(96, mload(0x2c)))
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                     HOOKS TO OVERRIDE                      */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Hook that is called before any transfer of tokens.
    /// This includes minting and burning.
    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.
    function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {}
}
          

Compiler Settings

{"outputSelection":{"*":{"*":["*"],"":["*"]}},"optimizer":{"runs":999999,"enabled":true},"libraries":{},"evmVersion":"paris"}
              

Contract ABI

[{"type":"error","name":"ArgumentInvalid","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256[]","name":"","internalType":"uint256[]"}],"name":"allAllowancesForManyAccounts","inputs":[{"type":"address[]","name":"userAddresses","internalType":"address[]"},{"type":"address","name":"spenderAddress","internalType":"address"},{"type":"address[]","name":"tokenAddresses","internalType":"address[]"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256[]","name":"","internalType":"uint256[]"}],"name":"allBalancesForManyAccounts","inputs":[{"type":"address[]","name":"userAddresses","internalType":"address[]"},{"type":"address[]","name":"tokenAddresses","internalType":"address[]"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool[]","name":"","internalType":"bool[]"}],"name":"getNoncesUsed","inputs":[{"type":"address[]","name":"signerWallets","internalType":"address[]"},{"type":"uint256[]","name":"nonces","internalType":"uint256[]"},{"type":"address","name":"swapContract","internalType":"contract ISwap"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool[]","name":"","internalType":"bool[]"}],"name":"getOrdersValid","inputs":[{"type":"address","name":"senderWallet","internalType":"address"},{"type":"tuple[]","name":"orders","internalType":"struct ISwap.Order[]","components":[{"type":"uint256","name":"nonce","internalType":"uint256"},{"type":"uint256","name":"expiry","internalType":"uint256"},{"type":"tuple","name":"signer","internalType":"struct Party","components":[{"type":"address","name":"wallet","internalType":"address"},{"type":"address","name":"token","internalType":"address"},{"type":"bytes4","name":"kind","internalType":"bytes4"},{"type":"uint256","name":"id","internalType":"uint256"},{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"tuple","name":"sender","internalType":"struct Party","components":[{"type":"address","name":"wallet","internalType":"address"},{"type":"address","name":"token","internalType":"address"},{"type":"bytes4","name":"kind","internalType":"bytes4"},{"type":"uint256","name":"id","internalType":"uint256"},{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"address","name":"affiliateWallet","internalType":"address"},{"type":"uint256","name":"affiliateAmount","internalType":"uint256"},{"type":"uint8","name":"v","internalType":"uint8"},{"type":"bytes32","name":"r","internalType":"bytes32"},{"type":"bytes32","name":"s","internalType":"bytes32"}]},{"type":"address","name":"swapContract","internalType":"contract ISwap"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool[]","name":"","internalType":"bool[]"}],"name":"getOrdersValidERC20","inputs":[{"type":"address","name":"senderWallet","internalType":"address"},{"type":"tuple[]","name":"orders","internalType":"struct ISwapERC20.OrderERC20[]","components":[{"type":"uint256","name":"nonce","internalType":"uint256"},{"type":"uint256","name":"expiry","internalType":"uint256"},{"type":"address","name":"signerWallet","internalType":"address"},{"type":"address","name":"signerToken","internalType":"address"},{"type":"uint256","name":"signerAmount","internalType":"uint256"},{"type":"address","name":"senderWallet","internalType":"address"},{"type":"address","name":"senderToken","internalType":"address"},{"type":"uint256","name":"senderAmount","internalType":"uint256"},{"type":"uint8","name":"v","internalType":"uint8"},{"type":"bytes32","name":"r","internalType":"bytes32"},{"type":"bytes32","name":"s","internalType":"bytes32"}]},{"type":"address","name":"swapERC20Contract","internalType":"contract ISwapERC20"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address[][]","name":"","internalType":"address[][]"}],"name":"getTokensForStakers","inputs":[{"type":"address[]","name":"stakers","internalType":"address[]"},{"type":"address","name":"registryContract","internalType":"contract IRegistry"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"tokenAllowance","inputs":[{"type":"address","name":"userAddress","internalType":"address"},{"type":"address","name":"spenderAddress","internalType":"address"},{"type":"address","name":"tokenAddress","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"tokenBalance","inputs":[{"type":"address","name":"userAddress","internalType":"address"},{"type":"address","name":"tokenAddress","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256[]","name":"","internalType":"uint256[]"}],"name":"walletAllowances","inputs":[{"type":"address","name":"userAddress","internalType":"address"},{"type":"address","name":"spenderAddress","internalType":"address"},{"type":"address[]","name":"tokenAddresses","internalType":"address[]"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256[]","name":"","internalType":"uint256[]"}],"name":"walletBalances","inputs":[{"type":"address","name":"userAddress","internalType":"address"},{"type":"address[]","name":"tokenAddresses","internalType":"address[]"}]}]
              

Contract Creation Code

0x608060405234801561001057600080fd5b50611c59806100206000396000f3fe608060405234801561001057600080fd5b50600436106100be5760003560e01c80634c6305801161007657806377a7d9681161005b57806377a7d968146101865780638e8758d814610199578063df8344fe146101ac57600080fd5b80634c6305801461016057806359c94ce41461017357600080fd5b806317c958eb116100a757806317c958eb1461010d5780631e61290d1461012d578063366dff9c1461014d57600080fd5b806301afd5f3146100c35780631049334f146100ec575b600080fd5b6100d66100d13660046111ac565b6101bf565b6040516100e39190611218565b60405180910390f35b6100ff6100fa366004611291565b61037f565b6040519081526020016100e3565b61012061011b3660046112ca565b610529565b6040516100e39190611366565b61014061013b3660046113a0565b6106ac565b6040516100e391906113f7565b6100d661015b3660046114b5565b610867565b61012061016e36600461150e565b610943565b610120610181366004611585565b610b8b565b6100d6610194366004611609565b610d78565b6100ff6101a736600461165e565b610ebf565b6100d66101ba36600461169e565b611079565b606060006101cd8584611752565b67ffffffffffffffff8111156101e5576101e5611769565b60405190808252806020026020018201604052801561020e578160200160208202803683370190505b50905060005b858110156103755760005b8481101561036c57600086868381811061023b5761023b611798565b905060200201602081019061025091906117c7565b73ffffffffffffffffffffffffffffffffffffffff16146102f3576102bc88888481811061028057610280611798565b905060200201602081019061029591906117c7565b8787848181106102a7576102a7611798565b90506020020160208101906100fa91906117c7565b83826102c88886611752565b6102d291906117e4565b815181106102e2576102e2611798565b602002602001018181525050610364565b87878381811061030557610305611798565b905060200201602081019061031a91906117c7565b73ffffffffffffffffffffffffffffffffffffffff1631838261033d8886611752565b61034791906117e4565b8151811061035757610357611798565b6020026020010181815250505b60010161021f565b50600101610214565b5095945050505050565b600073ffffffffffffffffffffffffffffffffffffffff82163b1561051f576040805173ffffffffffffffffffffffffffffffffffffffff85811660248084019190915283518084039091018152604490920183526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f70a0823100000000000000000000000000000000000000000000000000000000179052915184926000929084169161043191906117f7565b600060405180830381855afa9150503d806000811461046c576040519150601f19603f3d011682016040523d82523d6000602084013e610471565b606091505b505090508015610514576040517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff86811660048301528316906370a0823190602401602060405180830381865afa1580156104e7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061050b9190611826565b92505050610523565b600092505050610523565b5060005b92915050565b606082610562576040517fc9f345a300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008367ffffffffffffffff81111561057d5761057d611769565b6040519080825280602002602001820160405280156105a6578160200160208202803683370190505b50905060005b848110156103755760008473ffffffffffffffffffffffffffffffffffffffff166349b8a932898989868181106105e5576105e5611798565b905061022002016040518363ffffffff1660e01b81526004016106099291906118d9565b600060405180830381865afa158015610626573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820160405261066c9190810190611a2a565b9050805160001461067e576000610681565b60015b83838151811061069357610693611798565b91151560209283029190910190910152506001016105ac565b606060008390036106e9576040517fc9f345a300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008367ffffffffffffffff81111561070457610704611769565b60405190808252806020026020018201604052801561073757816020015b60608152602001906001900390816107225790505b50905060005b8481101561085c578373ffffffffffffffffffffffffffffffffffffffff1663a6dfcd4987878481811061077357610773611798565b905060200201602081019061078891906117c7565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b16815273ffffffffffffffffffffffffffffffffffffffff9091166004820152602401600060405180830381865afa1580156107f1573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526108379190810190611ac0565b82828151811061084957610849611798565b602090810291909101015260010161073d565b5090505b9392505050565b6060816108a0576040517fc9f345a300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008267ffffffffffffffff8111156108bb576108bb611769565b6040519080825280602002602001820160405280156108e4578160200160208202803683370190505b50905060005b838110156103755761091e878787878581811061090957610909611798565b90506020020160208101906101a791906117c7565b82828151811061093057610930611798565b60209081029190910101526001016108ea565b60608261097c576040517fc9f345a300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008367ffffffffffffffff81111561099757610997611769565b6040519080825280602002602001820160405280156109c0578160200160208202803683370190505b50905060005b848110156103755760008686838181106109e2576109e2611798565b905061016002018036038101906109f99190611b4f565b905060008573ffffffffffffffffffffffffffffffffffffffff1663b9cb01b08a846000015185602001518660400151876060015188608001518960c001518a60e001518b61010001518c61012001518d61014001516040518c63ffffffff1660e01b8152600401610ae79b9a9998979695949392919073ffffffffffffffffffffffffffffffffffffffff9b8c168152602081019a909a5260408a01989098529589166060890152938816608088015260a087019290925290951660c085015260e084019490945260ff939093166101008301526101208201929092526101408101919091526101600190565b600060405180830381865afa158015610b04573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052610b4a9190810190611a2a565b90508051600014610b5c576000610b5f565b60015b848481518110610b7157610b71611798565b9115156020928302919091019091015250506001016109c6565b60606000859003610bc8576040517fc9f345a300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b848314610c01576040517fc9f345a300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008567ffffffffffffffff811115610c1c57610c1c611769565b604051908082528060200260200182016040528015610c45578160200160208202803683370190505b50905060005b86811015610d6d578373ffffffffffffffffffffffffffffffffffffffff16631647795e898984818110610c8157610c81611798565b9050602002016020810190610c9691906117c7565b888885818110610ca857610ca8611798565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e087901b16815273ffffffffffffffffffffffffffffffffffffffff90941660048501526020029190910135602483015250604401602060405180830381865afa158015610d1f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d439190611c01565b828281518110610d5557610d55611798565b91151560209283029190910190910152600101610c4b565b509695505050505050565b606081610db1576040517fc9f345a300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008267ffffffffffffffff811115610dcc57610dcc611769565b604051908082528060200260200182016040528015610df5578160200160208202803683370190505b50905060005b8381101561085c576000858583818110610e1757610e17611798565b9050602002016020810190610e2c91906117c7565b73ffffffffffffffffffffffffffffffffffffffff1614610e8057610e5d868686848181106102a7576102a7611798565b828281518110610e6f57610e6f611798565b602002602001018181525050610eb7565b8573ffffffffffffffffffffffffffffffffffffffff1631828281518110610eaa57610eaa611798565b6020026020010181815250505b600101610dfb565b600073ffffffffffffffffffffffffffffffffffffffff82163b1561106f576040805173ffffffffffffffffffffffffffffffffffffffff868116602483015285811660448084019190915283518084039091018152606490920183526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fdd62ed3e000000000000000000000000000000000000000000000000000000001790529151849260009290841691610f7991906117f7565b600060405180830381855afa9150503d8060008114610fb4576040519150601f19603f3d011682016040523d82523d6000602084013e610fb9565b606091505b505090508015611064576040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8781166004830152868116602483015283169063dd62ed3e90604401602060405180830381865afa158015611037573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061105b9190611826565b92505050610860565b600092505050610860565b5060009392505050565b606060006110878684611752565b67ffffffffffffffff81111561109f5761109f611769565b6040519080825280602002602001820160405280156110c8578160200160208202803683370190505b50905060005b86811015610d6d5760005b848110156111575761111e8989848181106110f6576110f6611798565b905060200201602081019061110b91906117c7565b8888888581811061090957610909611798565b838261112a8886611752565b61113491906117e4565b8151811061114457611144611798565b60209081029190910101526001016110d9565b506001016110ce565b60008083601f84011261117257600080fd5b50813567ffffffffffffffff81111561118a57600080fd5b6020830191508360208260051b85010111156111a557600080fd5b9250929050565b600080600080604085870312156111c257600080fd5b843567ffffffffffffffff808211156111da57600080fd5b6111e688838901611160565b909650945060208701359150808211156111ff57600080fd5b5061120c87828801611160565b95989497509550505050565b6020808252825182820181905260009190848201906040850190845b8181101561125057835183529284019291840191600101611234565b50909695505050505050565b73ffffffffffffffffffffffffffffffffffffffff8116811461127e57600080fd5b50565b803561128c8161125c565b919050565b600080604083850312156112a457600080fd5b82356112af8161125c565b915060208301356112bf8161125c565b809150509250929050565b600080600080606085870312156112e057600080fd5b84356112eb8161125c565b9350602085013567ffffffffffffffff8082111561130857600080fd5b818701915087601f83011261131c57600080fd5b81358181111561132b57600080fd5b8860206102208302850101111561134157600080fd5b602083019550809450505050604085013561135b8161125c565b939692955090935050565b6020808252825182820181905260009190848201906040850190845b81811015611250578351151583529284019291840191600101611382565b6000806000604084860312156113b557600080fd5b833567ffffffffffffffff8111156113cc57600080fd5b6113d886828701611160565b90945092505060208401356113ec8161125c565b809150509250925092565b6000602080830181845280855180835260408601915060408160051b87010192508387016000805b838110156114a7578886037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0018552825180518088529088019088880190845b8181101561149157835173ffffffffffffffffffffffffffffffffffffffff168352928a0192918a019160010161145f565b509097505050938601939186019160010161141f565b509398975050505050505050565b600080600080606085870312156114cb57600080fd5b84356114d68161125c565b935060208501356114e68161125c565b9250604085013567ffffffffffffffff81111561150257600080fd5b61120c87828801611160565b6000806000806060858703121561152457600080fd5b843561152f8161125c565b9350602085013567ffffffffffffffff8082111561154c57600080fd5b818701915087601f83011261156057600080fd5b81358181111561156f57600080fd5b8860206101608302850101111561134157600080fd5b60008060008060006060868803121561159d57600080fd5b853567ffffffffffffffff808211156115b557600080fd5b6115c189838a01611160565b909750955060208801359150808211156115da57600080fd5b506115e788828901611160565b90945092505060408601356115fb8161125c565b809150509295509295909350565b60008060006040848603121561161e57600080fd5b83356116298161125c565b9250602084013567ffffffffffffffff81111561164557600080fd5b61165186828701611160565b9497909650939450505050565b60008060006060848603121561167357600080fd5b833561167e8161125c565b9250602084013561168e8161125c565b915060408401356113ec8161125c565b6000806000806000606086880312156116b657600080fd5b853567ffffffffffffffff808211156116ce57600080fd5b6116da89838a01611160565b9097509550602088013591506116ef8261125c565b9093506040870135908082111561170557600080fd5b5061171288828901611160565b969995985093965092949392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b808202811582820484141761052357610523611723565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6000602082840312156117d957600080fd5b81356108608161125c565b8082018082111561052357610523611723565b6000825160005b8181101561181857602081860181015185830152016117fe565b506000920191825250919050565b60006020828403121561183857600080fd5b5051919050565b803561184a8161125c565b73ffffffffffffffffffffffffffffffffffffffff90811683526020820135906118738261125c565b16602083015260408101357fffffffff0000000000000000000000000000000000000000000000000000000081168082146118ad57600080fd5b60408401525060608181013590830152608090810135910152565b803560ff8116811461128c57600080fd5b60006102408201905073ffffffffffffffffffffffffffffffffffffffff8085168352833560208401526020840135604084015261191d606084016040860161183f565b61192e610100840160e0860161183f565b61018084013561193d8161125c565b166101a0838101919091528301356101c0808401919091526119608482016118c8565b90506101e060ff821681850152610200915080850135828501525080840135610220840152509392505050565b604051610160810167ffffffffffffffff811182821017156119b1576119b1611769565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff811182821017156119fe576119fe611769565b604052919050565b600067ffffffffffffffff821115611a2057611a20611769565b5060051b60200190565b60006020808385031215611a3d57600080fd5b825167ffffffffffffffff811115611a5457600080fd5b8301601f81018513611a6557600080fd5b8051611a78611a7382611a06565b6119b7565b81815260059190911b82018301908381019087831115611a9757600080fd5b928401925b82841015611ab557835182529284019290840190611a9c565b979650505050505050565b60006020808385031215611ad357600080fd5b825167ffffffffffffffff811115611aea57600080fd5b8301601f81018513611afb57600080fd5b8051611b09611a7382611a06565b81815260059190911b82018301908381019087831115611b2857600080fd5b928401925b82841015611ab5578351611b408161125c565b82529284019290840190611b2d565b60006101608284031215611b6257600080fd5b611b6a61198d565b8235815260208301356020820152611b8460408401611281565b6040820152611b9560608401611281565b606082015260808301356080820152611bb060a08401611281565b60a0820152611bc160c08401611281565b60c082015260e083013560e0820152610100611bde8185016118c8565b908201526101208381013590820152610140928301359281019290925250919050565b600060208284031215611c1357600080fd5b8151801515811461086057600080fdfea2646970667358221220927d0199d54c98894549aa24916c4d5ff18c42b05e8a851d973d01ab71d7b78064736f6c63430008170033

Deployed ByteCode

0x608060405234801561001057600080fd5b50600436106100be5760003560e01c80634c6305801161007657806377a7d9681161005b57806377a7d968146101865780638e8758d814610199578063df8344fe146101ac57600080fd5b80634c6305801461016057806359c94ce41461017357600080fd5b806317c958eb116100a757806317c958eb1461010d5780631e61290d1461012d578063366dff9c1461014d57600080fd5b806301afd5f3146100c35780631049334f146100ec575b600080fd5b6100d66100d13660046111ac565b6101bf565b6040516100e39190611218565b60405180910390f35b6100ff6100fa366004611291565b61037f565b6040519081526020016100e3565b61012061011b3660046112ca565b610529565b6040516100e39190611366565b61014061013b3660046113a0565b6106ac565b6040516100e391906113f7565b6100d661015b3660046114b5565b610867565b61012061016e36600461150e565b610943565b610120610181366004611585565b610b8b565b6100d6610194366004611609565b610d78565b6100ff6101a736600461165e565b610ebf565b6100d66101ba36600461169e565b611079565b606060006101cd8584611752565b67ffffffffffffffff8111156101e5576101e5611769565b60405190808252806020026020018201604052801561020e578160200160208202803683370190505b50905060005b858110156103755760005b8481101561036c57600086868381811061023b5761023b611798565b905060200201602081019061025091906117c7565b73ffffffffffffffffffffffffffffffffffffffff16146102f3576102bc88888481811061028057610280611798565b905060200201602081019061029591906117c7565b8787848181106102a7576102a7611798565b90506020020160208101906100fa91906117c7565b83826102c88886611752565b6102d291906117e4565b815181106102e2576102e2611798565b602002602001018181525050610364565b87878381811061030557610305611798565b905060200201602081019061031a91906117c7565b73ffffffffffffffffffffffffffffffffffffffff1631838261033d8886611752565b61034791906117e4565b8151811061035757610357611798565b6020026020010181815250505b60010161021f565b50600101610214565b5095945050505050565b600073ffffffffffffffffffffffffffffffffffffffff82163b1561051f576040805173ffffffffffffffffffffffffffffffffffffffff85811660248084019190915283518084039091018152604490920183526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f70a0823100000000000000000000000000000000000000000000000000000000179052915184926000929084169161043191906117f7565b600060405180830381855afa9150503d806000811461046c576040519150601f19603f3d011682016040523d82523d6000602084013e610471565b606091505b505090508015610514576040517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff86811660048301528316906370a0823190602401602060405180830381865afa1580156104e7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061050b9190611826565b92505050610523565b600092505050610523565b5060005b92915050565b606082610562576040517fc9f345a300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008367ffffffffffffffff81111561057d5761057d611769565b6040519080825280602002602001820160405280156105a6578160200160208202803683370190505b50905060005b848110156103755760008473ffffffffffffffffffffffffffffffffffffffff166349b8a932898989868181106105e5576105e5611798565b905061022002016040518363ffffffff1660e01b81526004016106099291906118d9565b600060405180830381865afa158015610626573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820160405261066c9190810190611a2a565b9050805160001461067e576000610681565b60015b83838151811061069357610693611798565b91151560209283029190910190910152506001016105ac565b606060008390036106e9576040517fc9f345a300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008367ffffffffffffffff81111561070457610704611769565b60405190808252806020026020018201604052801561073757816020015b60608152602001906001900390816107225790505b50905060005b8481101561085c578373ffffffffffffffffffffffffffffffffffffffff1663a6dfcd4987878481811061077357610773611798565b905060200201602081019061078891906117c7565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b16815273ffffffffffffffffffffffffffffffffffffffff9091166004820152602401600060405180830381865afa1580156107f1573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526108379190810190611ac0565b82828151811061084957610849611798565b602090810291909101015260010161073d565b5090505b9392505050565b6060816108a0576040517fc9f345a300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008267ffffffffffffffff8111156108bb576108bb611769565b6040519080825280602002602001820160405280156108e4578160200160208202803683370190505b50905060005b838110156103755761091e878787878581811061090957610909611798565b90506020020160208101906101a791906117c7565b82828151811061093057610930611798565b60209081029190910101526001016108ea565b60608261097c576040517fc9f345a300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008367ffffffffffffffff81111561099757610997611769565b6040519080825280602002602001820160405280156109c0578160200160208202803683370190505b50905060005b848110156103755760008686838181106109e2576109e2611798565b905061016002018036038101906109f99190611b4f565b905060008573ffffffffffffffffffffffffffffffffffffffff1663b9cb01b08a846000015185602001518660400151876060015188608001518960c001518a60e001518b61010001518c61012001518d61014001516040518c63ffffffff1660e01b8152600401610ae79b9a9998979695949392919073ffffffffffffffffffffffffffffffffffffffff9b8c168152602081019a909a5260408a01989098529589166060890152938816608088015260a087019290925290951660c085015260e084019490945260ff939093166101008301526101208201929092526101408101919091526101600190565b600060405180830381865afa158015610b04573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052610b4a9190810190611a2a565b90508051600014610b5c576000610b5f565b60015b848481518110610b7157610b71611798565b9115156020928302919091019091015250506001016109c6565b60606000859003610bc8576040517fc9f345a300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b848314610c01576040517fc9f345a300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008567ffffffffffffffff811115610c1c57610c1c611769565b604051908082528060200260200182016040528015610c45578160200160208202803683370190505b50905060005b86811015610d6d578373ffffffffffffffffffffffffffffffffffffffff16631647795e898984818110610c8157610c81611798565b9050602002016020810190610c9691906117c7565b888885818110610ca857610ca8611798565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e087901b16815273ffffffffffffffffffffffffffffffffffffffff90941660048501526020029190910135602483015250604401602060405180830381865afa158015610d1f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d439190611c01565b828281518110610d5557610d55611798565b91151560209283029190910190910152600101610c4b565b509695505050505050565b606081610db1576040517fc9f345a300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008267ffffffffffffffff811115610dcc57610dcc611769565b604051908082528060200260200182016040528015610df5578160200160208202803683370190505b50905060005b8381101561085c576000858583818110610e1757610e17611798565b9050602002016020810190610e2c91906117c7565b73ffffffffffffffffffffffffffffffffffffffff1614610e8057610e5d868686848181106102a7576102a7611798565b828281518110610e6f57610e6f611798565b602002602001018181525050610eb7565b8573ffffffffffffffffffffffffffffffffffffffff1631828281518110610eaa57610eaa611798565b6020026020010181815250505b600101610dfb565b600073ffffffffffffffffffffffffffffffffffffffff82163b1561106f576040805173ffffffffffffffffffffffffffffffffffffffff868116602483015285811660448084019190915283518084039091018152606490920183526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fdd62ed3e000000000000000000000000000000000000000000000000000000001790529151849260009290841691610f7991906117f7565b600060405180830381855afa9150503d8060008114610fb4576040519150601f19603f3d011682016040523d82523d6000602084013e610fb9565b606091505b505090508015611064576040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8781166004830152868116602483015283169063dd62ed3e90604401602060405180830381865afa158015611037573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061105b9190611826565b92505050610860565b600092505050610860565b5060009392505050565b606060006110878684611752565b67ffffffffffffffff81111561109f5761109f611769565b6040519080825280602002602001820160405280156110c8578160200160208202803683370190505b50905060005b86811015610d6d5760005b848110156111575761111e8989848181106110f6576110f6611798565b905060200201602081019061110b91906117c7565b8888888581811061090957610909611798565b838261112a8886611752565b61113491906117e4565b8151811061114457611144611798565b60209081029190910101526001016110d9565b506001016110ce565b60008083601f84011261117257600080fd5b50813567ffffffffffffffff81111561118a57600080fd5b6020830191508360208260051b85010111156111a557600080fd5b9250929050565b600080600080604085870312156111c257600080fd5b843567ffffffffffffffff808211156111da57600080fd5b6111e688838901611160565b909650945060208701359150808211156111ff57600080fd5b5061120c87828801611160565b95989497509550505050565b6020808252825182820181905260009190848201906040850190845b8181101561125057835183529284019291840191600101611234565b50909695505050505050565b73ffffffffffffffffffffffffffffffffffffffff8116811461127e57600080fd5b50565b803561128c8161125c565b919050565b600080604083850312156112a457600080fd5b82356112af8161125c565b915060208301356112bf8161125c565b809150509250929050565b600080600080606085870312156112e057600080fd5b84356112eb8161125c565b9350602085013567ffffffffffffffff8082111561130857600080fd5b818701915087601f83011261131c57600080fd5b81358181111561132b57600080fd5b8860206102208302850101111561134157600080fd5b602083019550809450505050604085013561135b8161125c565b939692955090935050565b6020808252825182820181905260009190848201906040850190845b81811015611250578351151583529284019291840191600101611382565b6000806000604084860312156113b557600080fd5b833567ffffffffffffffff8111156113cc57600080fd5b6113d886828701611160565b90945092505060208401356113ec8161125c565b809150509250925092565b6000602080830181845280855180835260408601915060408160051b87010192508387016000805b838110156114a7578886037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0018552825180518088529088019088880190845b8181101561149157835173ffffffffffffffffffffffffffffffffffffffff168352928a0192918a019160010161145f565b509097505050938601939186019160010161141f565b509398975050505050505050565b600080600080606085870312156114cb57600080fd5b84356114d68161125c565b935060208501356114e68161125c565b9250604085013567ffffffffffffffff81111561150257600080fd5b61120c87828801611160565b6000806000806060858703121561152457600080fd5b843561152f8161125c565b9350602085013567ffffffffffffffff8082111561154c57600080fd5b818701915087601f83011261156057600080fd5b81358181111561156f57600080fd5b8860206101608302850101111561134157600080fd5b60008060008060006060868803121561159d57600080fd5b853567ffffffffffffffff808211156115b557600080fd5b6115c189838a01611160565b909750955060208801359150808211156115da57600080fd5b506115e788828901611160565b90945092505060408601356115fb8161125c565b809150509295509295909350565b60008060006040848603121561161e57600080fd5b83356116298161125c565b9250602084013567ffffffffffffffff81111561164557600080fd5b61165186828701611160565b9497909650939450505050565b60008060006060848603121561167357600080fd5b833561167e8161125c565b9250602084013561168e8161125c565b915060408401356113ec8161125c565b6000806000806000606086880312156116b657600080fd5b853567ffffffffffffffff808211156116ce57600080fd5b6116da89838a01611160565b9097509550602088013591506116ef8261125c565b9093506040870135908082111561170557600080fd5b5061171288828901611160565b969995985093965092949392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b808202811582820484141761052357610523611723565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6000602082840312156117d957600080fd5b81356108608161125c565b8082018082111561052357610523611723565b6000825160005b8181101561181857602081860181015185830152016117fe565b506000920191825250919050565b60006020828403121561183857600080fd5b5051919050565b803561184a8161125c565b73ffffffffffffffffffffffffffffffffffffffff90811683526020820135906118738261125c565b16602083015260408101357fffffffff0000000000000000000000000000000000000000000000000000000081168082146118ad57600080fd5b60408401525060608181013590830152608090810135910152565b803560ff8116811461128c57600080fd5b60006102408201905073ffffffffffffffffffffffffffffffffffffffff8085168352833560208401526020840135604084015261191d606084016040860161183f565b61192e610100840160e0860161183f565b61018084013561193d8161125c565b166101a0838101919091528301356101c0808401919091526119608482016118c8565b90506101e060ff821681850152610200915080850135828501525080840135610220840152509392505050565b604051610160810167ffffffffffffffff811182821017156119b1576119b1611769565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff811182821017156119fe576119fe611769565b604052919050565b600067ffffffffffffffff821115611a2057611a20611769565b5060051b60200190565b60006020808385031215611a3d57600080fd5b825167ffffffffffffffff811115611a5457600080fd5b8301601f81018513611a6557600080fd5b8051611a78611a7382611a06565b6119b7565b81815260059190911b82018301908381019087831115611a9757600080fd5b928401925b82841015611ab557835182529284019290840190611a9c565b979650505050505050565b60006020808385031215611ad357600080fd5b825167ffffffffffffffff811115611aea57600080fd5b8301601f81018513611afb57600080fd5b8051611b09611a7382611a06565b81815260059190911b82018301908381019087831115611b2857600080fd5b928401925b82841015611ab5578351611b408161125c565b82529284019290840190611b2d565b60006101608284031215611b6257600080fd5b611b6a61198d565b8235815260208301356020820152611b8460408401611281565b6040820152611b9560608401611281565b606082015260808301356080820152611bb060a08401611281565b60a0820152611bc160c08401611281565b60c082015260e083013560e0820152610100611bde8185016118c8565b908201526101208381013590820152610140928301359281019290925250919050565b600060208284031215611c1357600080fd5b8151801515811461086057600080fdfea2646970667358221220927d0199d54c98894549aa24916c4d5ff18c42b05e8a851d973d01ab71d7b78064736f6c63430008170033