ETH Price: $1,898.53 (+0.59%)

Transaction Decoder

Block:
21989224 at Mar-06-2025 05:04:59 PM +UTC
Transaction Fee:
0.000585995783743095 ETH $1.11
Gas Used:
173,041 Gas / 3.386456295 Gwei

Emitted Events:

235 0x1da37120b111f02761681731ad5c934ae388d1a3.0x96052a141a2f0c4cf748a4b3ab448892252b6663103a9e1e9be4daced3044d48( 0x96052a141a2f0c4cf748a4b3ab448892252b6663103a9e1e9be4daced3044d48, 000000000000000000000000000000000000000000000000000000001e0a6e00, 0000000000000000000000000000000000000000000000000000000067c9d5bb, 0000000000000000000000000000000000000000000000000000000000000002, 0000000000000000000000000000000000000000000000000000000000000080, 000000000000000000000000000000000000000000000000000000000000000a, 6b31587033506353597000000000000000000000000000000000000000000000 )
236 0x1da37120b111f02761681731ad5c934ae388d1a3.0xfea5b921a56202bb7f6a9f7ad785fbf05b10bcca0fde4034834eeff201983ce6( 0xfea5b921a56202bb7f6a9f7ad785fbf05b10bcca0fde4034834eeff201983ce6, 0x000000000000000000000000c78b5e5692ed3282986a8ddc6d6327fb094e6ea4, 000000000000000000000000000000000000000000000097c9ce4cf6d5c00000, 0000000000000000000000000000000000000000000000000000000067c9d5bb, 0000000000000000000000000000000000000000000000000000000000000002 )
237 0x1da37120b111f02761681731ad5c934ae388d1a3.0xfea5b921a56202bb7f6a9f7ad785fbf05b10bcca0fde4034834eeff201983ce6( 0xfea5b921a56202bb7f6a9f7ad785fbf05b10bcca0fde4034834eeff201983ce6, 0x000000000000000000000000c78b5e5692ed3282986a8ddc6d6327fb094e6ea4, 00000000000000000000000000000000000000000000016c4abbebea01000000, 0000000000000000000000000000000000000000000000000000000067c9d5bb, 0000000000000000000000000000000000000000000000000000000000000002 )
238 TetherToken.Transfer( from=[Sender] 0xc78b5e5692ed3282986a8ddc6d6327fb094e6ea4, to=0x263197adaBD72E12EC8e2523d4d04Db0889F41E6, value=5040095761 )
239 0x1da37120b111f02761681731ad5c934ae388d1a3.0x624ba27c07e5521fa346e1ec584e6f7986ead862a3fb3d7c20f041de07aceb91( 0x624ba27c07e5521fa346e1ec584e6f7986ead862a3fb3d7c20f041de07aceb91, 0x000000000000000000000000c78b5e5692ed3282986a8ddc6d6327fb094e6ea4, 0x000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7, 000000000000000000000000000000000000000000000000000000012c69c211, 000000000000000000000000000000000000000000000bdbc41e0348b3000000, 0000000000000000000000000000000000000000000000000000000000000002, 0000000000000000000000000000000000000000000000000000000067c9d5bb, 000000000000000000000000000000000000000000000204148a38e0d6c00000, 00000000000000000000000000000000000000000000013faa8a056a23700000, 0000000000000000000000000000000000000000000000000000000000000001, 0000000000000000000000000000000000000000000000000000000000000100, 000000000000000000000000000000000000000000000000000000000000000a, 6b31587033506353597000000000000000000000000000000000000000000000 )

Account State Difference:

  Address   Before After State Difference Code
0x1Da37120...Ae388D1A3
(beaverbuild)
6.730418217160667049 Eth6.730586066930667049 Eth0.00016784977
0xc78b5E56...B094e6eA4
0.132562237015493677 Eth
Nonce: 17
0.131976241231750582 Eth
Nonce: 18
0.000585995783743095
0xdAC17F95...13D831ec7

Execution Trace

0x1da37120b111f02761681731ad5c934ae388d1a3.4016ac42( )
  • PresaleClaiming.buyTokens( )
    • EACAggregatorProxy.STATICCALL( )
      • AccessControlledOCR2Aggregator.STATICCALL( )
      • EACAggregatorProxy.STATICCALL( )
        • AccessControlledOCR2Aggregator.STATICCALL( )
        • TetherToken.transferFrom( _from=0xc78b5E5692ed3282986a8Ddc6D6327fB094e6eA4, _to=0x263197adaBD72E12EC8e2523d4d04Db0889F41E6, _value=5040095761 )
          File 1 of 4: TetherToken
          pragma solidity ^0.4.17;
          
          /**
           * @title SafeMath
           * @dev Math operations with safety checks that throw on error
           */
          library SafeMath {
              function mul(uint256 a, uint256 b) internal pure returns (uint256) {
                  if (a == 0) {
                      return 0;
                  }
                  uint256 c = a * b;
                  assert(c / a == b);
                  return c;
              }
          
              function div(uint256 a, uint256 b) internal pure returns (uint256) {
                  // assert(b > 0); // Solidity automatically throws when dividing by 0
                  uint256 c = a / b;
                  // assert(a == b * c + a % b); // There is no case in which this doesn't hold
                  return c;
              }
          
              function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                  assert(b <= a);
                  return a - b;
              }
          
              function add(uint256 a, uint256 b) internal pure returns (uint256) {
                  uint256 c = a + b;
                  assert(c >= a);
                  return c;
              }
          }
          
          /**
           * @title Ownable
           * @dev The Ownable contract has an owner address, and provides basic authorization control
           * functions, this simplifies the implementation of "user permissions".
           */
          contract Ownable {
              address public owner;
          
              /**
                * @dev The Ownable constructor sets the original `owner` of the contract to the sender
                * account.
                */
              function Ownable() public {
                  owner = msg.sender;
              }
          
              /**
                * @dev Throws if called by any account other than the owner.
                */
              modifier onlyOwner() {
                  require(msg.sender == owner);
                  _;
              }
          
              /**
              * @dev Allows the current owner to transfer control of the contract to a newOwner.
              * @param newOwner The address to transfer ownership to.
              */
              function transferOwnership(address newOwner) public onlyOwner {
                  if (newOwner != address(0)) {
                      owner = newOwner;
                  }
              }
          
          }
          
          /**
           * @title ERC20Basic
           * @dev Simpler version of ERC20 interface
           * @dev see https://github.com/ethereum/EIPs/issues/20
           */
          contract ERC20Basic {
              uint public _totalSupply;
              function totalSupply() public constant returns (uint);
              function balanceOf(address who) public constant returns (uint);
              function transfer(address to, uint value) public;
              event Transfer(address indexed from, address indexed to, uint value);
          }
          
          /**
           * @title ERC20 interface
           * @dev see https://github.com/ethereum/EIPs/issues/20
           */
          contract ERC20 is ERC20Basic {
              function allowance(address owner, address spender) public constant returns (uint);
              function transferFrom(address from, address to, uint value) public;
              function approve(address spender, uint value) public;
              event Approval(address indexed owner, address indexed spender, uint value);
          }
          
          /**
           * @title Basic token
           * @dev Basic version of StandardToken, with no allowances.
           */
          contract BasicToken is Ownable, ERC20Basic {
              using SafeMath for uint;
          
              mapping(address => uint) public balances;
          
              // additional variables for use if transaction fees ever became necessary
              uint public basisPointsRate = 0;
              uint public maximumFee = 0;
          
              /**
              * @dev Fix for the ERC20 short address attack.
              */
              modifier onlyPayloadSize(uint size) {
                  require(!(msg.data.length < size + 4));
                  _;
              }
          
              /**
              * @dev transfer token for a specified address
              * @param _to The address to transfer to.
              * @param _value The amount to be transferred.
              */
              function transfer(address _to, uint _value) public onlyPayloadSize(2 * 32) {
                  uint fee = (_value.mul(basisPointsRate)).div(10000);
                  if (fee > maximumFee) {
                      fee = maximumFee;
                  }
                  uint sendAmount = _value.sub(fee);
                  balances[msg.sender] = balances[msg.sender].sub(_value);
                  balances[_to] = balances[_to].add(sendAmount);
                  if (fee > 0) {
                      balances[owner] = balances[owner].add(fee);
                      Transfer(msg.sender, owner, fee);
                  }
                  Transfer(msg.sender, _to, sendAmount);
              }
          
              /**
              * @dev Gets the balance of the specified address.
              * @param _owner The address to query the the balance of.
              * @return An uint representing the amount owned by the passed address.
              */
              function balanceOf(address _owner) public constant returns (uint balance) {
                  return balances[_owner];
              }
          
          }
          
          /**
           * @title Standard ERC20 token
           *
           * @dev Implementation of the basic standard token.
           * @dev https://github.com/ethereum/EIPs/issues/20
           * @dev Based oncode by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol
           */
          contract StandardToken is BasicToken, ERC20 {
          
              mapping (address => mapping (address => uint)) public allowed;
          
              uint public constant MAX_UINT = 2**256 - 1;
          
              /**
              * @dev Transfer tokens from one address to another
              * @param _from address The address which you want to send tokens from
              * @param _to address The address which you want to transfer to
              * @param _value uint the amount of tokens to be transferred
              */
              function transferFrom(address _from, address _to, uint _value) public onlyPayloadSize(3 * 32) {
                  var _allowance = allowed[_from][msg.sender];
          
                  // Check is not needed because sub(_allowance, _value) will already throw if this condition is not met
                  // if (_value > _allowance) throw;
          
                  uint fee = (_value.mul(basisPointsRate)).div(10000);
                  if (fee > maximumFee) {
                      fee = maximumFee;
                  }
                  if (_allowance < MAX_UINT) {
                      allowed[_from][msg.sender] = _allowance.sub(_value);
                  }
                  uint sendAmount = _value.sub(fee);
                  balances[_from] = balances[_from].sub(_value);
                  balances[_to] = balances[_to].add(sendAmount);
                  if (fee > 0) {
                      balances[owner] = balances[owner].add(fee);
                      Transfer(_from, owner, fee);
                  }
                  Transfer(_from, _to, sendAmount);
              }
          
              /**
              * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
              * @param _spender The address which will spend the funds.
              * @param _value The amount of tokens to be spent.
              */
              function approve(address _spender, uint _value) public onlyPayloadSize(2 * 32) {
          
                  // To change the approve amount you first have to reduce the addresses`
                  //  allowance to zero by calling `approve(_spender, 0)` if it is not
                  //  already 0 to mitigate the race condition described here:
                  //  https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
                  require(!((_value != 0) && (allowed[msg.sender][_spender] != 0)));
          
                  allowed[msg.sender][_spender] = _value;
                  Approval(msg.sender, _spender, _value);
              }
          
              /**
              * @dev Function to check the amount of tokens than an owner allowed to a spender.
              * @param _owner address The address which owns the funds.
              * @param _spender address The address which will spend the funds.
              * @return A uint specifying the amount of tokens still available for the spender.
              */
              function allowance(address _owner, address _spender) public constant returns (uint remaining) {
                  return allowed[_owner][_spender];
              }
          
          }
          
          
          /**
           * @title Pausable
           * @dev Base contract which allows children to implement an emergency stop mechanism.
           */
          contract Pausable is Ownable {
            event Pause();
            event Unpause();
          
            bool public paused = false;
          
          
            /**
             * @dev Modifier to make a function callable only when the contract is not paused.
             */
            modifier whenNotPaused() {
              require(!paused);
              _;
            }
          
            /**
             * @dev Modifier to make a function callable only when the contract is paused.
             */
            modifier whenPaused() {
              require(paused);
              _;
            }
          
            /**
             * @dev called by the owner to pause, triggers stopped state
             */
            function pause() onlyOwner whenNotPaused public {
              paused = true;
              Pause();
            }
          
            /**
             * @dev called by the owner to unpause, returns to normal state
             */
            function unpause() onlyOwner whenPaused public {
              paused = false;
              Unpause();
            }
          }
          
          contract BlackList is Ownable, BasicToken {
          
              /////// Getters to allow the same blacklist to be used also by other contracts (including upgraded Tether) ///////
              function getBlackListStatus(address _maker) external constant returns (bool) {
                  return isBlackListed[_maker];
              }
          
              function getOwner() external constant returns (address) {
                  return owner;
              }
          
              mapping (address => bool) public isBlackListed;
              
              function addBlackList (address _evilUser) public onlyOwner {
                  isBlackListed[_evilUser] = true;
                  AddedBlackList(_evilUser);
              }
          
              function removeBlackList (address _clearedUser) public onlyOwner {
                  isBlackListed[_clearedUser] = false;
                  RemovedBlackList(_clearedUser);
              }
          
              function destroyBlackFunds (address _blackListedUser) public onlyOwner {
                  require(isBlackListed[_blackListedUser]);
                  uint dirtyFunds = balanceOf(_blackListedUser);
                  balances[_blackListedUser] = 0;
                  _totalSupply -= dirtyFunds;
                  DestroyedBlackFunds(_blackListedUser, dirtyFunds);
              }
          
              event DestroyedBlackFunds(address _blackListedUser, uint _balance);
          
              event AddedBlackList(address _user);
          
              event RemovedBlackList(address _user);
          
          }
          
          contract UpgradedStandardToken is StandardToken{
              // those methods are called by the legacy contract
              // and they must ensure msg.sender to be the contract address
              function transferByLegacy(address from, address to, uint value) public;
              function transferFromByLegacy(address sender, address from, address spender, uint value) public;
              function approveByLegacy(address from, address spender, uint value) public;
          }
          
          contract TetherToken is Pausable, StandardToken, BlackList {
          
              string public name;
              string public symbol;
              uint public decimals;
              address public upgradedAddress;
              bool public deprecated;
          
              //  The contract can be initialized with a number of tokens
              //  All the tokens are deposited to the owner address
              //
              // @param _balance Initial supply of the contract
              // @param _name Token Name
              // @param _symbol Token symbol
              // @param _decimals Token decimals
              function TetherToken(uint _initialSupply, string _name, string _symbol, uint _decimals) public {
                  _totalSupply = _initialSupply;
                  name = _name;
                  symbol = _symbol;
                  decimals = _decimals;
                  balances[owner] = _initialSupply;
                  deprecated = false;
              }
          
              // Forward ERC20 methods to upgraded contract if this one is deprecated
              function transfer(address _to, uint _value) public whenNotPaused {
                  require(!isBlackListed[msg.sender]);
                  if (deprecated) {
                      return UpgradedStandardToken(upgradedAddress).transferByLegacy(msg.sender, _to, _value);
                  } else {
                      return super.transfer(_to, _value);
                  }
              }
          
              // Forward ERC20 methods to upgraded contract if this one is deprecated
              function transferFrom(address _from, address _to, uint _value) public whenNotPaused {
                  require(!isBlackListed[_from]);
                  if (deprecated) {
                      return UpgradedStandardToken(upgradedAddress).transferFromByLegacy(msg.sender, _from, _to, _value);
                  } else {
                      return super.transferFrom(_from, _to, _value);
                  }
              }
          
              // Forward ERC20 methods to upgraded contract if this one is deprecated
              function balanceOf(address who) public constant returns (uint) {
                  if (deprecated) {
                      return UpgradedStandardToken(upgradedAddress).balanceOf(who);
                  } else {
                      return super.balanceOf(who);
                  }
              }
          
              // Forward ERC20 methods to upgraded contract if this one is deprecated
              function approve(address _spender, uint _value) public onlyPayloadSize(2 * 32) {
                  if (deprecated) {
                      return UpgradedStandardToken(upgradedAddress).approveByLegacy(msg.sender, _spender, _value);
                  } else {
                      return super.approve(_spender, _value);
                  }
              }
          
              // Forward ERC20 methods to upgraded contract if this one is deprecated
              function allowance(address _owner, address _spender) public constant returns (uint remaining) {
                  if (deprecated) {
                      return StandardToken(upgradedAddress).allowance(_owner, _spender);
                  } else {
                      return super.allowance(_owner, _spender);
                  }
              }
          
              // deprecate current contract in favour of a new one
              function deprecate(address _upgradedAddress) public onlyOwner {
                  deprecated = true;
                  upgradedAddress = _upgradedAddress;
                  Deprecate(_upgradedAddress);
              }
          
              // deprecate current contract if favour of a new one
              function totalSupply() public constant returns (uint) {
                  if (deprecated) {
                      return StandardToken(upgradedAddress).totalSupply();
                  } else {
                      return _totalSupply;
                  }
              }
          
              // Issue a new amount of tokens
              // these tokens are deposited into the owner address
              //
              // @param _amount Number of tokens to be issued
              function issue(uint amount) public onlyOwner {
                  require(_totalSupply + amount > _totalSupply);
                  require(balances[owner] + amount > balances[owner]);
          
                  balances[owner] += amount;
                  _totalSupply += amount;
                  Issue(amount);
              }
          
              // Redeem tokens.
              // These tokens are withdrawn from the owner address
              // if the balance must be enough to cover the redeem
              // or the call will fail.
              // @param _amount Number of tokens to be issued
              function redeem(uint amount) public onlyOwner {
                  require(_totalSupply >= amount);
                  require(balances[owner] >= amount);
          
                  _totalSupply -= amount;
                  balances[owner] -= amount;
                  Redeem(amount);
              }
          
              function setParams(uint newBasisPoints, uint newMaxFee) public onlyOwner {
                  // Ensure transparency by hardcoding limit beyond which fees can never be added
                  require(newBasisPoints < 20);
                  require(newMaxFee < 50);
          
                  basisPointsRate = newBasisPoints;
                  maximumFee = newMaxFee.mul(10**decimals);
          
                  Params(basisPointsRate, maximumFee);
              }
          
              // Called when new token are issued
              event Issue(uint amount);
          
              // Called when tokens are redeemed
              event Redeem(uint amount);
          
              // Called when contract is deprecated
              event Deprecate(address newAddress);
          
              // Called if contract ever adds fees
              event Params(uint feeBasisPoints, uint maxFee);
          }

          File 2 of 4: PresaleClaiming
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.25;
          import {IPresale} from "./interfaces/IPresale.sol";
          import {IClaiming} from "./interfaces/IClaiming.sol";
          import {IERC20} from "openzeppelin/token/ERC20/IERC20.sol";
          import {ERC20} from "openzeppelin/token/ERC20/ERC20.sol";
          import {SafeERC20} from "openzeppelin/token/ERC20/utils/SafeERC20.sol";
          import {UUPSUpgradeable} from "openzeppelin-upgradeable/contracts/proxy/utils/UUPSUpgradeable.sol";
          import {AggregatorV3Interface} from "chainlink/shared/interfaces/AggregatorV3Interface.sol";
          import {MerkleProof} from "openzeppelin/utils/cryptography/MerkleProof.sol";
          import {AccessControlUpgradeable} from "openzeppelin-upgradeable/contracts/access/AccessControlUpgradeable.sol";
          contract PresaleClaiming is
          IClaiming,
          IPresale,
          UUPSUpgradeable,
          AccessControlUpgradeable {
              using SafeERC20 for IERC20;
              uint256 private constant _RETURN_AMOUNT_THRESHOLD = 1e14;
              uint8 private constant _STABLE_COIN_DECIMALS = 6;
              uint8 private constant _BASE_DECIMALS = 18;
              uint256 private constant _ONE_ETHER = 1 ether;
              uint256 private constant _PERCENTAGE_DIVISOR = 10000;
              bytes32 public constant SERVER_ROLE = keccak256("SERVER_ROLE");
              bytes32 public referralMerkleRoot;
              bytes32 public claimingMerkleRoot;
              uint256 public tokensSold;
              uint256 public totalUsdAmountRaised;
              uint256 public claimingStartTimestamp;
              uint256 public minimumPurchaseUsdAmount;
              bool public volumeBuyBonusEnabled;
              bool public nativeCoinEnabled;
              bool public claimingActive;
              bool private _claimingSet;
              address public treasuryWallet;
              address public usdcStableCoinAddress;
              address public usdtStableCoinAddress;
              uint8 public currentPresalePhaseIndex;
              uint8 private _totalPhases;
              uint8 public claimingPhaseIndex;
              IERC20 public claimableToken;
              AggregatorV3Interface public nativeCoinDataFeed;
              IPresale.PresaleState public presaleState;
              mapping(address => bool) public userToFirstPurchaseMade;
              mapping(address => bool) public isSupportedPaymentToken;
              mapping(address => AggregatorV3Interface) public tokenDataFeed;
              mapping(address => bool) public wertWallets;
              mapping(address => uint256) public userToAmountTokensBought;
              mapping(address => uint256) public userToBonusTokensAcquired;
              mapping(address => uint256) public userToClaimedAmount;
              mapping(address => ClaimInfo[]) public userToClaimInfo;
              ReferralBonusInfo public referralBonus;
              mapping(ReferralBonusTier => ReferralBonusTierInfo) public referralBonusTiers;
              mapping(VolumeBuyBonusTier => VolumeBuyBonusTierInfo) public volumeBuyBonusTiers;
              ClaimingPhase[] public claimingPhases;
              TokensInfoPerPhase[3] public tokensInfoPerPhase;
              mapping(address => uint256) public tokenDataFeedHeartbeat;
              uint256 public nativeCoinDataFeedHeartbeat;
              uint256 public presalePhaseStartTimestamp;
              uint256[50] private __gap;
              constructor() {
                  _disableInitializers();
              }
              modifier withPresaleActive() {
                  if (presaleState != PresaleState.ACTIVE) {
                      revert PresaleNotActiveError();
                  }
                  _;
              }
              modifier withPhaseActive(){
                  if (presalePhaseStartTimestamp > block.timestamp) {
                      revert PresalePhaseNotActiveError();
                  }
                  _;
              }
              modifier withClaimingActive() {
                  if (!claimingActive || block.timestamp < claimingStartTimestamp) {
                      revert ClaimingNotActiveError();
                  }
                  _;
              }
              modifier withClaimingSet(bool activating) {
                  if (!_claimingSet && activating) {
                      revert ClaimingNotSetError();
                  }
                  _;
              }
              modifier withNativeCoinEnabled(){
                  if (!nativeCoinEnabled) {
                      revert NativeCoinNotEnabledError();
                  }
                  _;
              }
              modifier withValidHeartbeat(uint256 _heartbeat){
                  if (_heartbeat > 86400) {
                      revert InvalidHeartbeatError();
                  }
                  _;
              }
              /**
               * @notice Allows a user to claim tokens based on their eligibility and the provided Merkle proof.
               * @dev This function checks the user's eligibility, updates the claiming phase index, and transfers the claimable amount.
               * @param merkleProof The Merkle proof verifying the user's eligibility.
               * @param amount The amount to claim based on the Merkle proof.
               */
              function claimTokens(bytes32[] calldata merkleProof, uint256 amount) external override withClaimingActive {
                  address user = msg.sender;
                  bool isValidProof = _isValidProof(user, merkleProof, amount);
                  uint256 totalClaimAmount = _calculateTotalClaimAmount(isValidProof, amount, user);
                  if (totalClaimAmount == 0) {
                      revert NoTokensToClaimError();
                  }
                  _updateClaimingPhaseIndex();
                  uint256 claimableAmount = 0;
                  claimableAmount = _isLastClaimingPhase()
                      ? totalClaimAmount
                      : (totalClaimAmount * claimingPhases[claimingPhaseIndex].phasePercentage) / _PERCENTAGE_DIVISOR;
                  if (claimableAmount == 0) {
                      revert NoTokensToClaimError();
                  }
                  uint256 amountToClaim = claimableAmount - userToClaimedAmount[user];
                  if (amountToClaim == 0) {
                      revert NoTokensToClaimError();
                  }
                  userToClaimInfo[user].push(ClaimInfo(amountToClaim, claimingPhaseIndex, block.timestamp));
                  userToClaimedAmount[user] += amountToClaim;
                  claimableToken.safeTransfer(user, amountToClaim);
                  emit TokensClaimedEvent(user, amountToClaim);
              }
          /**
           * @notice Calculates the claimable amount for a user based on their eligibility and the provided Merkle proof.
           * @param user The address of the user.
           * @param merkleProof The Merkle proof verifying the user's eligibility.
           * @param amount The amount to claim based on the Merkle proof.
           * @return The claimable amount for the user.
           */
              function getClaimableAmount(address user, bytes32[] calldata merkleProof, uint256 amount) external view returns (uint256) {
                  bool isValidProof = _isValidProof(user, merkleProof, amount);
                  uint256 totalClaimAmount = _calculateTotalClaimAmount(isValidProof, amount, user);
                  if (totalClaimAmount == 0) {
                      return 0;
                  }
                  uint8 localClaimPhaseIndex = getLocalClaimPhaseIndex();
                  uint256 claimableAmount = (localClaimPhaseIndex == claimingPhases.length - 1)
                      ? totalClaimAmount
                      : (totalClaimAmount * claimingPhases[localClaimPhaseIndex].phasePercentage) / _PERCENTAGE_DIVISOR;
                  if (claimableAmount == 0) {
                      return 0;
                  }
                  return claimableAmount - userToClaimedAmount[user];
              }
              /**
               * @notice Returns the current local claim phase index based on the current timestamp.
               * @return The current local claim phase index.
               */
              function getLocalClaimPhaseIndex() public view returns (uint8) {
                  uint8 localPhaseIndex = claimingPhaseIndex;
                  while (localPhaseIndex < claimingPhases.length - 1 &&
                      block.timestamp >= claimingPhases[localPhaseIndex].phaseEnd) {
                      localPhaseIndex++;
                  }
                  return localPhaseIndex;
              }
              /**
               * @notice Returns the current presale phase index.
               * @return The current presale phase index.
               */
              function getPresalePhaseIndex() external view returns (uint8) {
                  return currentPresalePhaseIndex;
              }
              /**
               * @notice Executes a token purchase, allowing payments through either native cryptocurrency or ERC20 tokens.
               * @dev This function handles various checks and operations, such as referral code usage, enforcing minimum purchase amounts,
               *      processing bonuses, and supporting purchases on behalf of other users under specific conditions.
               * @param _referralCode A string representing the referral code used for the transaction, which may affect bonuses.
               * @param _merkleProofReferral A Merkle proof to verify the validity of the referral code within a presale context.
               * @param _amountToBuy The exact amount of tokens the user wants to purchase.
               * @param _paymentTokenAddress The ERC20 token address used for the payment. If this is the zero address, native currency (e.g., ETH) is used.
               */
              function buyTokens(
                  string calldata _referralCode,
                  bytes32[] calldata _merkleProofReferral,
                  uint256 _amountToBuy,
                  address _paymentTokenAddress
              ) external payable override withPresaleActive withPhaseActive {
                  uint256 valueSent = msg.value;
                  address user = msg.sender;
                  (uint256 exchangeRate, uint8 decimals) = _computeExchangeRate(_paymentTokenAddress);
                  uint256 cost = _getCostForTokens(_amountToBuy, exchangeRate);
                  uint256 usdAmount = _computeUsdAmount(_amountToBuy);
                  if (_isBelowMinimumPurchase(usdAmount)) {
                      revert MinimumPurchaseError();
                  }
                  (uint256 bonusTokens, bool isValidProof) = _processBonuses(_referralCode, _merkleProofReferral, _amountToBuy, usdAmount, user);
                  usdAmount += _computeUsdAmount(bonusTokens);
                  totalUsdAmountRaised += usdAmount;
                  userToAmountTokensBought[user] += _amountToBuy;
                  if (!userToFirstPurchaseMade[user]) {
                      userToFirstPurchaseMade[user] = true;
                  }
                  if (valueSent > 0 && _paymentTokenAddress == address(0)) {
                      if (valueSent < cost) {
                          revert InsufficientFundsError(cost, valueSent);
                      }
                      _buyTokensWithNativeCoin(_amountToBuy, cost, user, bonusTokens, usdAmount, isValidProof, _referralCode, valueSent);
                  } else if (isSupportedPaymentToken[_paymentTokenAddress] && valueSent == 0) {
                      IERC20 paymentToken = IERC20(_paymentTokenAddress);
                      _buyTokensWithERC20(paymentToken, _amountToBuy, cost, user, decimals, bonusTokens, usdAmount, isValidProof, _referralCode);
                  } else {
                      revert UnsupportedERC20PaymentTokenError(_paymentTokenAddress);
                  }
              }
              /**
               * @notice Executes a token purchase on behalf of another user through Wert integration, allowing payments through native cryptocurrency.
               * @dev This function handles various checks and operations, such as referral code usage, enforcing minimum purchase amounts, and processing bonuses.
               * @param _referralCode A string representing the referral code used for the transaction, which may affect bonuses.
               * @param _merkleProofReferral A Merkle proof to verify the validity of the referral code within a presale context.
               * @param _userToBuyFor The address of the user for whom the tokens are being bought.
               */
              function buyTokensWert(
                  string calldata _referralCode,
                  bytes32[] calldata _merkleProofReferral,
                  address _userToBuyFor
              ) external payable withPresaleActive withPhaseActive {
                  uint256 valueSent = msg.value;
                  if (valueSent == 0) {
                      revert ZeroValueError();
                  }
                  address user;
                  if (_userToBuyFor == address(0)) {
                      revert InvalidTokenRecipientAddressError(_userToBuyFor);
                  } else {
                      if (!wertWallets[msg.sender]) {
                          revert InvalidSenderAddressError(msg.sender);
                      }
                      user = _userToBuyFor;
                  }
                  (uint256 exchangeRate,) = _computeExchangeRate(address(0));
                  uint256 tokensBought = _getTokensForCost(valueSent, exchangeRate);
                  uint256 usdAmount = _computeUsdAmount(tokensBought);
                  if (_isBelowMinimumPurchase(usdAmount)) {
                      revert MinimumPurchaseError();
                  }
                  (uint256 bonusTokens, bool isValidProof) = _processBonuses(_referralCode, _merkleProofReferral, tokensBought, usdAmount, user);
                  if (!userToFirstPurchaseMade[user]) {
                      userToFirstPurchaseMade[user] = true;
                  }
                  usdAmount += _computeUsdAmount(bonusTokens);
                  totalUsdAmountRaised += usdAmount;
                  userToAmountTokensBought[user] += tokensBought;
                  _buyTokensWithNativeCoin(tokensBought, valueSent, user, bonusTokens, usdAmount, isValidProof, _referralCode, valueSent);
                  emit TokensBoughtWertEvent(user, valueSent, tokensBought, currentPresalePhaseIndex, block.timestamp, bonusTokens, usdAmount, isValidProof, _referralCode);
              }
              /**
               * @notice Returns the token information for a given presale phase.
               * @param phaseIndex The index of the presale phase.
               * @return The token information for the specified phase.
               */
              function getTokensInfoPerPhase(uint8 phaseIndex) external view returns (TokensInfoPerPhase memory) {
                  return tokensInfoPerPhase[phaseIndex];
              }
              /**
               * @notice Calculates the cost and tokens receivable based on either the amount to pay or tokens to buy.
               * @dev This function allows for a flexible exchange calculation where the user can specify either
               *      the amount of tokens they want to buy or the amount they want to pay in a specified payment token.
               *      It accounts for different token decimals and applies relevant exchange rates and bonuses.
               * @param paymentToken The address of the token used for payment.
               * @param tokensToBuy The number of tokens the buyer wants to purchase. If set to 0, the function
               *        calculates tokens based on `amountToPay`.
               * @param amountToPay The amount of payment tokens the buyer wishes to spend. If set to 0, the function
               *        calculates the cost based on `tokensToBuy`.
               * @return cost The total cost in the payment token.
               * @return tokensBought The total number of tokens that can be bought with the given `amountToPay` or
               *         the number specified in `tokensToBuy`.
               * @return usdAmount The USD equivalent of the cost calculated.
               * @return volumeBuyBonusTokens The additional tokens given as a bonus based on the volume of the transaction.
               * @return referredBonusTokens The additional tokens given as a bonus for referred purchases.
               */
              function getCost(
                  address paymentToken,
                  uint256 tokensToBuy,
                  uint256 amountToPay
              ) external view returns (uint256 cost, uint256 tokensBought, uint256 usdAmount, uint256 volumeBuyBonusTokens, uint256 referredBonusTokens) {
                  uint256 exchangeRate;
                  uint256 decimals = _BASE_DECIMALS;
                  if (paymentToken != address(0) && _isStableCoin(paymentToken)) {
                      decimals = _STABLE_COIN_DECIMALS;
                  }
                  exchangeRate = _getExchangeRate(paymentToken);
                  if (amountToPay > 0) {
                      tokensBought = _getTokensForCost(amountToPay, exchangeRate);
                      cost = amountToPay;
                  } else {
                      cost = _getCostForTokens(tokensToBuy, exchangeRate);
                      tokensBought = tokensToBuy;
                  }
                  usdAmount = _computeUsdAmount(tokensBought);
                  if (decimals < _BASE_DECIMALS) {
                      cost = cost / 10 ** (_BASE_DECIMALS - decimals);
                  }
                  if (cost == 0) {
                      revert ZeroValueError();
                  }
                  (, referredBonusTokens) = _getReferralBonusTokens(tokensBought, usdAmount);
                  (volumeBuyBonusTokens) = _getVolumeBuyBonusTokens(tokensBought, usdAmount);
              }
              /**
               * @notice Sets the referral bonus tier information.
               * @dev This function can only be called by the admin.
               * @param referralBonusTier The tier of the referral bonus.
               * @param referrerPercentage The percentage bonus for the referrer.
               * @param referredPercentage The percentage bonus for the referred.
               * @param thresholdUsdAmount The threshold USD amount for the bonus to be applicable.
               */
              function setReferralBonusTier(
                  ReferralBonusTier referralBonusTier,
                  uint16 referrerPercentage,
                  uint16 referredPercentage,
                  uint128 thresholdUsdAmount
              ) external onlyRole(DEFAULT_ADMIN_ROLE) {
                  referralBonusTiers[referralBonusTier] = ReferralBonusTierInfo(referrerPercentage, referredPercentage, thresholdUsdAmount);
                  emit ReferralBonusTierSetEvent(referralBonusTier, referrerPercentage, referredPercentage, thresholdUsdAmount);
              }
          /**
           * @notice Sets the referral bonus parameters.
           * @dev This function can only be called by the admin.
           * @param onlyFirstPurchase Boolean indicating if the bonus is awarded only for the first purchase.
           * @param enabled Boolean indicating if the referral bonus is enabled.
           */
              function setReferralBonus(
                  bool onlyFirstPurchase,
                  bool enabled
              ) external onlyRole(DEFAULT_ADMIN_ROLE) {
                  referralBonus.awardOnlyFirstPurchase = onlyFirstPurchase;
                  referralBonus.enabled = enabled;
                  emit ReferralBonusSetEvent(onlyFirstPurchase, enabled);
              }
              /**
               * @notice Enables or disables the volume buy bonus.
               * @dev This function can only be called by the admin.
               * @param _enabled Boolean indicating if the volume buy bonus is enabled.
               */
              function setVolumeBuyBonusEnabled(bool _enabled) external onlyRole(DEFAULT_ADMIN_ROLE) {
                  volumeBuyBonusEnabled = _enabled;
                  emit VolumeBuyBonusEnabledEvent(_enabled);
              }
              /**
               * @notice Sets the volume buy bonus tier information.
               * @dev This function can only be called by the admin.
               * @param _volumeBuyBonusTier The tier of the volume buy bonus.
               * @param _bonusPercentage The percentage bonus for the volume buy.
               * @param _thresholdUsdAmount The threshold USD amount for the bonus to be applicable.
               */
              function setVolumeBuyBonusTier(
                  VolumeBuyBonusTier _volumeBuyBonusTier,
                  uint16 _bonusPercentage,
                  uint128 _thresholdUsdAmount
              ) external onlyRole(DEFAULT_ADMIN_ROLE) {
                  volumeBuyBonusTiers[_volumeBuyBonusTier] = VolumeBuyBonusTierInfo(_bonusPercentage, _thresholdUsdAmount);
                  emit VolumeBuyBonusTierSetEvent(_volumeBuyBonusTier, _bonusPercentage, _thresholdUsdAmount);
              }
              /**
               * @notice Sets the state of the presale.
               * @dev This function can only be called by the admin.
               * @param _presaleState The new state of the presale.
               */
              function setPresaleState(PresaleState _presaleState) external onlyRole(DEFAULT_ADMIN_ROLE) {
                  presaleState = _presaleState;
                  emit PresaleStateChangedEvent(_presaleState);
              }
              /**
               * @notice Activates or deactivates the claiming process.
               * @dev This function can only be called by the admin.
               * @param active Boolean indicating if claiming is active.
               */
              function setClaimingActive(bool active) external onlyRole(DEFAULT_ADMIN_ROLE) withClaimingSet(active) {
                  claimingActive = active;
                  emit ClaimingStateChangedEvent(active);
              }
              /**
               * @notice Sets the claiming configuration including vesting durations, percentages, start timestamp, and Merkle root.
               * @dev This function can only be called by the admin.
               * @param _claimingPhaseVestingDurations Array of vesting durations for each claiming phase.
               * @param _claimingPhasePercentages Array of percentages for each claiming phase.
               * @param _claimingStartTimestamp The start timestamp for the claiming process.
               * @param _merkleRoot The Merkle root used for verifying claims.
               */
              function setClaiming(
                  uint256[] calldata _claimingPhaseVestingDurations,
                  uint16[] calldata _claimingPhasePercentages,
                  uint256 _claimingStartTimestamp,
                  bytes32 _merkleRoot
              ) external onlyRole(DEFAULT_ADMIN_ROLE) {
                  uint256 claimingPhaseVestingDurationsLength = _claimingPhaseVestingDurations.length;
                  if (claimingPhaseVestingDurationsLength != _claimingPhasePercentages.length) {
                      revert InvalidClaimingDataError();
                  }
                  _claimingSet = true;
                  uint256 cumulativeVestingDuration = _claimingStartTimestamp;
                  delete claimingPhases;
                  for (uint8 i = 0; i < claimingPhaseVestingDurationsLength;) {
                      if (_claimingPhasePercentages[i] > 10000) {
                          revert InvalidClaimingPercentagesError();
                      }
                      cumulativeVestingDuration += _claimingPhaseVestingDurations[i];
                      claimingPhases.push(ClaimingPhase(cumulativeVestingDuration, _claimingPhasePercentages[i]));
                      unchecked {
                          ++i;
                      }
                  }
                  claimingMerkleRoot = _merkleRoot;
                  claimingStartTimestamp = _claimingStartTimestamp;
                  emit ClaimingSetEvent(_claimingStartTimestamp, _claimingPhaseVestingDurations, _claimingPhasePercentages, _merkleRoot);
              }
              /**
               * @notice Sets the payment token and its enabled status.
               * @dev This function can only be called by the admin. The token must have the correct number of decimals.
               * @param _paymentToken The address of the payment token.
               * @param enabled Boolean indicating if the payment token is enabled.
               */
              function setPaymentToken(address _paymentToken, bool enabled) external onlyRole(DEFAULT_ADMIN_ROLE) {
                  if (ERC20(_paymentToken).decimals() != _BASE_DECIMALS) {
                      revert InvalidPaymentTokenError(_paymentToken);
                  }
                  isSupportedPaymentToken[_paymentToken] = enabled;
                  emit PaymentTokenSetEvent(_paymentToken, enabled);
              }
              /**
               * @notice Updates the address of the treasury wallet.
               * @dev This function can only be called by the admin. The treasury wallet address cannot be the zero address.
               * @param _treasuryWallet The new address of the treasury wallet.
               */
              function updateTreasuryWallet(address _treasuryWallet) external onlyRole(DEFAULT_ADMIN_ROLE) {
                  if (_treasuryWallet == address(0)) {
                      revert ZeroAddressError();
                  }
                  treasuryWallet = _treasuryWallet;
                  emit TreasuryWalletUpdatedEvent(_treasuryWallet);
              }
              /**
               * @notice Updates the data feed address for a specific token.
               * @dev This function can only be called by the admin.
               * @param _token The address of the token.
               * @param _dataFeed The address of the data feed.
               * @param _heartbeat The heartbeat for the data feed.
               */
              function updateTokenDataFeed(address _token, address _dataFeed, uint256 _heartbeat)
              withValidHeartbeat(_heartbeat)
              external onlyRole(DEFAULT_ADMIN_ROLE) {
                  if (_token == address(0) || _dataFeed == address(0)) {
                      revert ZeroAddressError();
                  }
                  tokenDataFeed[_token] = AggregatorV3Interface(_dataFeed);
                  tokenDataFeedHeartbeat[_token] = _heartbeat;
                  emit TokenDataFeedUpdatedEvent(_token, _dataFeed, _heartbeat);
              }
              /**
               * @notice Updates the data feed address for the native coin.
               * @dev This function can only be called by the admin.
               * @param _dataFeed The address of the data feed.
               * @param _heartbeat The heartbeat for the data feed.
               */
              function updateNativeCoinDataFeed(address _dataFeed, uint256 _heartbeat)
              withValidHeartbeat(_heartbeat)
              external onlyRole(DEFAULT_ADMIN_ROLE) {
                  if (_dataFeed == address(0)) {
                      revert ZeroAddressError();
                  }
                  nativeCoinDataFeed = AggregatorV3Interface(_dataFeed);
                  nativeCoinDataFeedHeartbeat = _heartbeat;
                  emit NativeCoinDataFeedUpdatedEvent(_dataFeed, _heartbeat);
              }
              /**
               * @notice Allows the admin to withdraw tokens in case of an emergency.
               * @dev This function can only be called by the admin.
               * @param amountToWithdraw The amount of tokens to withdraw.
               */
              function emergencyWithdrawToken(uint256 amountToWithdraw) external onlyRole(DEFAULT_ADMIN_ROLE) {
                  address user = msg.sender;
                  claimableToken.safeTransfer(user, amountToWithdraw);
                  emit TokensWithdrawnEvent(user, amountToWithdraw);
              }
              /**
               * @notice Allows the admin to withdraw native coins (ETH) in case of an emergency.
               * @dev This function can only be called by the admin. The recipient address cannot be the zero address.
               * @param _to The address to receive the withdrawn native coins.
               * @param amountToWithdraw The amount of native coins to withdraw.
               */
              function emergencyWithdrawNativeCoin(address payable _to, uint256 amountToWithdraw) external onlyRole(DEFAULT_ADMIN_ROLE) {
                  if (_to == address(0)) {
                      revert ZeroAddressError();
                  }
                  uint256 balance = address(this).balance;
                  if (amountToWithdraw > balance) {
                      revert InsufficientFundsError(balance, amountToWithdraw);
                  }
                  (bool success,) = _to.call{value: amountToWithdraw}("");
                  if (!success) {
                      revert WithdrawalFailedError();
                  }
                  emit NativeCoinWithdrawnEvent(_to, amountToWithdraw);
              }
              /**
               * @notice Updates the Merkle root for the referral system.
               */
              function updateReferralMerkleRoot(bytes32 _referralMerkleRoot) external onlyRole(DEFAULT_ADMIN_ROLE) {
                  referralMerkleRoot = _referralMerkleRoot;
                  emit ReferralMerkleRootUpdatedEvent(_referralMerkleRoot);
              }
              /**
               * @notice Updates the Merkle root for the claiming system.
               */
              function updateClaimingMerkleRoot(bytes32 _claimingMerkleRoot) external onlyRole(DEFAULT_ADMIN_ROLE) {
                  claimingMerkleRoot = _claimingMerkleRoot;
                  emit ClaimingMerkleRootUpdatedEvent(_claimingMerkleRoot);
              }
              /**
               * @notice Sets an address as a hot wallet.
               */
              function setHotWalletAddress(address _hotWalletAddress) external onlyRole(DEFAULT_ADMIN_ROLE) {
                  if (_hotWalletAddress == address(0)) {
                      revert ZeroAddressError();
                  }
                  wertWallets[_hotWalletAddress] = true;
                  emit HotWalletAddressSetEvent(_hotWalletAddress);
              }
              /**
               * @notice Sets an address for estimation purposes.
               */
              function setEstimationAddress(address _estimationAddress) external onlyRole(DEFAULT_ADMIN_ROLE) {
                  if (_estimationAddress == address(0)) {
                      revert ZeroAddressError();
                  }
                  wertWallets[_estimationAddress] = true;
                  emit EstimationAddressSetEvent(_estimationAddress);
              }
              /**
               * @notice Sets the current presale phase by index.
               */
              function setPresalePhase(uint8 _phaseIndex) external onlyRole(SERVER_ROLE) {
                  if (_phaseIndex > _totalPhases - 1) {
                      presaleState = PresaleState.FINISHED;
                      return;
                  }
                  presaleState = PresaleState.COMING_SOON;
                  currentPresalePhaseIndex = _phaseIndex;
                  emit PresalePhaseSetEvent(_phaseIndex);
              }
              /**
               * @notice Sets the address of the USDC stablecoin contract and enables or disables it as a payment token.
               */
              function setUsdcStableCoinAddress(address _usdcStableCoinAddress, bool enabled) external onlyRole(DEFAULT_ADMIN_ROLE) {
                  if (ERC20(_usdcStableCoinAddress).decimals() != _STABLE_COIN_DECIMALS) {
                      revert InvalidPaymentTokenError(_usdcStableCoinAddress);
                  }
                  isSupportedPaymentToken[usdcStableCoinAddress] = false;
                  usdcStableCoinAddress = _usdcStableCoinAddress;
                  isSupportedPaymentToken[_usdcStableCoinAddress] = enabled;
                  emit PaymentTokenSetEvent(_usdcStableCoinAddress, enabled);
              }
              /**
               * @notice Sets the address of the USDT stablecoin contract and enables or disables it as a payment token.
               */
              function setUsdtStableCoinAddress(address _usdtStableCoinAddress, bool enabled) external onlyRole(DEFAULT_ADMIN_ROLE) {
                  if (ERC20(_usdtStableCoinAddress).decimals() != _STABLE_COIN_DECIMALS) {
                      revert InvalidPaymentTokenError(_usdtStableCoinAddress);
                  }
                  isSupportedPaymentToken[usdtStableCoinAddress] = false;
                  usdtStableCoinAddress = _usdtStableCoinAddress;
                  isSupportedPaymentToken[_usdtStableCoinAddress] = enabled;
                  emit PaymentTokenSetEvent(_usdtStableCoinAddress, enabled);
              }
              /**
               * @notice Returns the total number of presale phases.
               * @return The total number of presale phases.
               */
              function getTotalPresalePhases() external view returns (uint8) {
                  return _totalPhases;
              }
              /**
               * @notice Sets the minimum purchase USD amount.
               * @dev This function can only be called by the admin.
               * @param _minimumPurchaseUsdAmount The new minimum purchase USD amount.
               */
              function setMinimumPurchaseUsdAmount(uint256 _minimumPurchaseUsdAmount) external onlyRole(DEFAULT_ADMIN_ROLE) {
                  minimumPurchaseUsdAmount = _minimumPurchaseUsdAmount;
                  emit MinimumPurchaseUsdAmountSetEvent(_minimumPurchaseUsdAmount);
              }
              /**
               * @notice Sets the address of the token that can be claimed.
               * @dev This function can only be called by the admin. The token address cannot be the zero address.
               * @param _token The address of the claimable token.
               */
              function setClaimableToken(address _token) external onlyRole(DEFAULT_ADMIN_ROLE) {
                  if (_token == address(0)) {
                      revert ZeroAddressError();
                  }
                  claimableToken = IERC20(_token);
                  emit ClaimableTokenSetEvent(_token);
              }
              function setPresalePhaseStartTimestamp(uint256 _presalePhaseStartTimestamp) external onlyRole(DEFAULT_ADMIN_ROLE) {
                  presalePhaseStartTimestamp = _presalePhaseStartTimestamp;
                  presaleState = PresaleState.ACTIVE;
                  emit PresalePhaseStartTimestampSetEvent(_presalePhaseStartTimestamp);
              }
              /**
               * @notice Initializes the contract with the specified parameters.
               * @dev This function is an initializer and can only be called once. It sets up payment, treasury wallet, roles, claimable token, and other configurations.
               * @param _nativeCoinDataFeed The address of the native coin data feed.
               * @param _nativeCoinDataFeedHeartbeat The heartbeat for the native coin data feed.
               * @param _treasuryWallet The address of the treasury wallet.
               * @param _tokenPricesPerPhase Array of token prices per phase.
               * @param _tokenAddress The address of the token.
               * @param _hotWalletAddress The address of the hot wallet.
               * @param _estimationAddress The address of the estimation address.
               * @param _minimumPurchaseUsdAmount The minimum purchase USD amount.
               * @param _serverAddress The address of the server with SERVER_ROLE.
               * @param _nativeCoinEnabled Boolean indicating if the native coin is enabled for payments.
               */
              function initialize(
                  address _nativeCoinDataFeed,
                  uint256 _nativeCoinDataFeedHeartbeat,
                  address _treasuryWallet,
                  uint128[] calldata _tokenPricesPerPhase,
                  address _tokenAddress,
                  address _hotWalletAddress,
                  address _estimationAddress,
                  uint256 _minimumPurchaseUsdAmount,
                  address _serverAddress,
                  bool _nativeCoinEnabled
              ) external initializer {
                  __AccessControl_init();
                  __UUPSUpgradeable_init();
                  _initializePayment(_nativeCoinDataFeed, _nativeCoinDataFeedHeartbeat);
                  _initializeTreasuryWallet(_treasuryWallet);
                  _initializeRoles(_serverAddress);
                  claimableToken = IERC20(_tokenAddress);
                  uint256 tokenPricesPerPhaseLength = _tokenPricesPerPhase.length;
                  if (tokenPricesPerPhaseLength > tokensInfoPerPhase.length) {
                      revert ExceedsMaxPhasesError();
                  }
                  for (uint8 i = 0; i < tokenPricesPerPhaseLength;) {
                      tokensInfoPerPhase[i] = TokensInfoPerPhase(_tokenPricesPerPhase[i], uint128(0));
                      unchecked {
                          ++i;
                      }
                  }
                  presaleState = PresaleState.COMING_SOON;
                  _totalPhases = uint8(tokenPricesPerPhaseLength);
                  currentPresalePhaseIndex = 0;
                  wertWallets[_hotWalletAddress] = true;
                  wertWallets[_estimationAddress] = true;
                  minimumPurchaseUsdAmount = _minimumPurchaseUsdAmount;
                  nativeCoinEnabled = _nativeCoinEnabled;
                  emit PresaleInitializedEvent(
                      _nativeCoinDataFeed,
                      _treasuryWallet,
                      _tokenPricesPerPhase,
                      _tokenAddress,
                      _hotWalletAddress,
                      _estimationAddress,
                      _minimumPurchaseUsdAmount,
                      _serverAddress,
                      _nativeCoinEnabled
                  );
              }
              /**
               * @notice Initializes the bonus tiers and settings for referral and volume buy bonuses.
               * @dev This function can only be called by the admin.
               * @param _referralBonusTier1 Information for referral bonus tier 1.
               * @param _referralBonusTier2 Information for referral bonus tier 2.
               * @param _onlyFirstPurchase Boolean indicating if the bonus is awarded only for the first purchase.
               * @param _referralBonusEnabled Boolean indicating if the referral bonus is enabled.
               * @param _volumeBuyBonusTier1 Information for volume buy bonus tier 1.
               * @param _volumeBuyBonusTier2 Information for volume buy bonus tier 2.
               * @param _volumeBuyBonusTier3 Information for volume buy bonus tier 3.
               * @param _volumeBuyBonusTier4 Information for volume buy bonus tier 4.
               * @param _volumeBuyBonusEnabled Boolean indicating if the volume buy bonus is enabled.
               */
              function initializeBonuses(
                  ReferralBonusTierInfo calldata _referralBonusTier1,
                  ReferralBonusTierInfo calldata _referralBonusTier2,
                  bool _onlyFirstPurchase,
                  bool _referralBonusEnabled,
                  VolumeBuyBonusTierInfo calldata _volumeBuyBonusTier1,
                  VolumeBuyBonusTierInfo calldata _volumeBuyBonusTier2,
                  VolumeBuyBonusTierInfo calldata _volumeBuyBonusTier3,
                  VolumeBuyBonusTierInfo calldata _volumeBuyBonusTier4,
                  bool _volumeBuyBonusEnabled
              ) external onlyRole(DEFAULT_ADMIN_ROLE) {
                  referralBonus = ReferralBonusInfo(
                      _onlyFirstPurchase,
                      _referralBonusEnabled
                  );
                  referralBonusTiers[ReferralBonusTier.TIER_1] = _referralBonusTier1;
                  referralBonusTiers[ReferralBonusTier.TIER_2] = _referralBonusTier2;
                  volumeBuyBonusTiers[VolumeBuyBonusTier.TIER_1] = _volumeBuyBonusTier1;
                  volumeBuyBonusTiers[VolumeBuyBonusTier.TIER_2] = _volumeBuyBonusTier2;
                  volumeBuyBonusTiers[VolumeBuyBonusTier.TIER_3] = _volumeBuyBonusTier3;
                  volumeBuyBonusTiers[VolumeBuyBonusTier.TIER_4] = _volumeBuyBonusTier4;
                  volumeBuyBonusEnabled = _volumeBuyBonusEnabled;
                  emit BonusesInitializedEvent(
                      _referralBonusTier1,
                      _referralBonusTier2,
                      _onlyFirstPurchase,
                      _referralBonusEnabled,
                      _volumeBuyBonusTier1,
                      _volumeBuyBonusTier2,
                      _volumeBuyBonusTier3,
                      _volumeBuyBonusTier4,
                      _volumeBuyBonusEnabled
                  );
              }
              /**
               * @notice Authorizes an upgrade to a new implementation address.
               * @dev This function is called internally and can only be called by the admin.
               * @param _newImplementation The address of the new implementation contract.
               */
              function _authorizeUpgrade(
                  address _newImplementation
              ) internal override onlyRole(DEFAULT_ADMIN_ROLE) {}
              /**
               * @notice Calculates the number of tokens that can be bought with a given amount and exchange rate.
               * @param amountToPay The amount to pay.
               * @param exchangeRate The exchange rate.
               * @return tokensBought The number of tokens bought.
               */
              function _getTokensForCost(
                  uint256 amountToPay,
                  uint256 exchangeRate
              ) private view returns (uint256 tokensBought) {
                  tokensBought = exchangeRate * amountToPay / tokensInfoPerPhase[currentPresalePhaseIndex].tokensPrice;
              }
              /**
               * @notice Calculates the cost for a given number of tokens and exchange rate.
               */
              function _getCostForTokens(
                  uint256 tokensToBuy,
                  uint256 exchangeRate
              ) private view returns (uint256 cost) {
                  cost = tokensToBuy * tokensInfoPerPhase[currentPresalePhaseIndex].tokensPrice / exchangeRate;
              }
              /**
               * @notice Retrieves the exchange rate for a given payment token.
               * @param paymentToken The address of the payment token.
               * @return exchangeRateWithEighteenDecimals The exchange rate adjusted to 18 decimals.
               */
              function _getExchangeRate(address paymentToken) private view returns (uint256 exchangeRateWithEighteenDecimals) {
                  int256 exchangeRate;
                  uint8 decimals;
                  if (paymentToken != address(0)) {
                      try tokenDataFeed[paymentToken].latestRoundData() returns (
                          uint80 /* roundId */,
                          int256 answer,
                          uint256 /* startedAt */,
                          uint256 updatedAt,
                          uint80 /* answeredInRound */
                      ) {
                          exchangeRate = answer;
                          decimals = tokenDataFeed[paymentToken].decimals();
                          if (block.timestamp - updatedAt > tokenDataFeedHeartbeat[paymentToken]) {
                              revert DataFeedHeartbeatError();
                          }
                      } catch {
                          revert InvalidTokenDataFeedError(address(tokenDataFeed[paymentToken]));
                      }
                  } else {
                      try nativeCoinDataFeed.latestRoundData() returns (
                          uint80 /* roundId */,
                          int256 answer,
                          uint256 /* startedAt */,
                          uint256 updatedAt,
                          uint80 /* answeredInRound */
                      ) {
                          exchangeRate = answer;
                          decimals = nativeCoinDataFeed.decimals();
                          if (block.timestamp - updatedAt > nativeCoinDataFeedHeartbeat) {
                              revert DataFeedHeartbeatError();
                          }
                      } catch {
                          revert InvalidTokenDataFeedError(address(nativeCoinDataFeed));
                      }
                  }
                  if (decimals == 0 || decimals > _BASE_DECIMALS) {
                      revert InvalidDecimalsError();
                  }
                  if (exchangeRate <= 0) {
                      revert InvalidExchangeRateError(uint256(exchangeRate));
                  }
                  exchangeRateWithEighteenDecimals = uint256(exchangeRate) * 10 ** (_BASE_DECIMALS - decimals);
              }
              /**
               * @notice Checks if a given token is a stablecoin.
               * @param _token The address of the token.
               * @return True if the token is a stablecoin, false otherwise.
               */
              function _isStableCoin(address _token) private view returns (bool) {
                  return _token == usdcStableCoinAddress || _token == usdtStableCoinAddress;
              }
              /**
               * @notice Processes the referral bonus for a given purchase.
               * @param referralCode The referral code used.
               * @param referralMerkleProof The Merkle proof for the referral code.
               * @param tokensBought The number of tokens bought.
               * @param usdAmount The USD amount of the purchase.
               * @param user The address of the user making the purchase.
               * @return bonusTokens The number of bonus tokens awarded.
               * @return isValidProof Boolean indicating if the referral proof is valid.
               */
              function _processReferralBonus(
                  string calldata referralCode,
                  bytes32[] calldata referralMerkleProof,
                  uint256 tokensBought,
                  uint256 usdAmount,
                  address user
              ) private returns (uint256 bonusTokens, bool isValidProof) {
                  bonusTokens = 0;
                  isValidProof = false;
                  if (!referralBonus.enabled) {
                      return (bonusTokens, isValidProof);
                  }
                  if (referralBonus.awardOnlyFirstPurchase && userToFirstPurchaseMade[user]) {
                      return (bonusTokens, isValidProof);
                  }
                  (uint256 referrerBonusUsdt, uint256 referredBonusTokens) = _getReferralBonusTokens(tokensBought, usdAmount);
                  if (referrerBonusUsdt == 0 && referredBonusTokens == 0) {
                      return (bonusTokens, isValidProof);
                  }
                  bytes32 node = keccak256(abi.encodePacked(referralCode));
                  isValidProof = MerkleProof.verifyCalldata(
                      referralMerkleProof,
                      referralMerkleRoot,
                      node
                  );
                  if (!isValidProof) {
                      return (bonusTokens, isValidProof);
                  }
                  if (referrerBonusUsdt > 0) {
                      emit ReferrerBonusEvent(referrerBonusUsdt, block.timestamp, currentPresalePhaseIndex, referralCode);
                  }
                  if (referredBonusTokens > 0) {
                      bonusTokens = referredBonusTokens;
                      _awardBonusTokens(referredBonusTokens, user);
                  }
              }
              /**
               * @notice Calculates the referral bonus tokens based on the amount bought and USD amount.
               * @param tokensBought The number of tokens bought.
               * @param usdAmount The USD amount of the purchase.
               * @return referrerBonusUsdt The USD bonus for the referrer.
               * @return referredBonusTokens The number of bonus tokens for the referred user.
               */
              function _getReferralBonusTokens(
                  uint256 tokensBought,
                  uint256 usdAmount
              ) private view returns (uint256 referrerBonusUsdt, uint256 referredBonusTokens){
                  uint16 referrerPercentage;
                  uint16 referredPercentage;
                  if (usdAmount > referralBonusTiers[ReferralBonusTier.TIER_2].thresholdUsdAmount) {
                      referrerPercentage = referralBonusTiers[ReferralBonusTier.TIER_2].referrerPercentage;
                      referredPercentage = referralBonusTiers[ReferralBonusTier.TIER_2].referredPercentage;
                  } else if (usdAmount > referralBonusTiers[ReferralBonusTier.TIER_1].thresholdUsdAmount) {
                      referrerPercentage = referralBonusTiers[ReferralBonusTier.TIER_1].referrerPercentage;
                      referredPercentage = referralBonusTiers[ReferralBonusTier.TIER_1].referredPercentage;
                  } else {
                      return (0, 0);
                  }
                  referrerBonusUsdt = ((usdAmount * referrerPercentage) / _PERCENTAGE_DIVISOR) / (10 ** (_BASE_DECIMALS - _STABLE_COIN_DECIMALS));
                  referredBonusTokens = (tokensBought * referredPercentage) / _PERCENTAGE_DIVISOR;
              }
              /**
               * @notice Awards bonus tokens to a user and updates the relevant state variables.
               * @dev This function is called internally to handle bonus token distribution.
               * @param bonusTokens The number of bonus tokens to award.
               * @param user The address of the user receiving the bonus tokens.
               */
              function _awardBonusTokens(uint256 bonusTokens, address user) private {
                  tokensSold += bonusTokens;
                  userToBonusTokensAcquired[user] += bonusTokens;
                  tokensInfoPerPhase[currentPresalePhaseIndex].tokensSold += bonusTokens;
                  emit BonusTokensEvent(user, bonusTokens, block.timestamp, currentPresalePhaseIndex);
              }
              /**
               * @notice Checks if the USD amount is below the minimum purchase amount.
               * @param usdAmount The USD amount to check.
               * @return True if the amount is below the minimum purchase amount, false otherwise.
               */
              function _isBelowMinimumPurchase(uint256 usdAmount) private view returns (bool) {
                  return usdAmount < minimumPurchaseUsdAmount;
              }
              /**
               * @notice Computes the exchange rate for a given payment token.
               * @param paymentToken The address of the payment token.
               * @return exchangeRate The exchange rate adjusted to 18 decimals.
               * @return decimals The number of decimals for the exchange rate.
               */
              function _computeExchangeRate(address paymentToken) private view returns (uint256 exchangeRate, uint8 decimals) {
                  if (paymentToken != address(0) && _isStableCoin(paymentToken)) {
                      decimals = _STABLE_COIN_DECIMALS;
                  } else {
                      decimals = _BASE_DECIMALS;
                  }
                  exchangeRate = _getExchangeRate(paymentToken);
              }
              /**
               * @notice Computes the USD amount for a given token amount.
               * @param amount The token amount.
               * @return The USD amount equivalent to the token amount.
               */
              function _computeUsdAmount(
                  uint256 amount
              ) private view returns (uint256) {
                  return amount * tokensInfoPerPhase[currentPresalePhaseIndex].tokensPrice / _ONE_ETHER;
              }
              /**
               * @notice Processes referral and volume buy bonuses for a given purchase.
               * @param referralCode The referral code used.
               * @param referralMerkleProof The Merkle proof for the referral code.
               * @param tokensBought The number of tokens bought.
               * @param usdAmount The USD amount of the purchase.
               * @param user The address of the user making the purchase.
               * @return bonusTokens The total number of bonus tokens awarded.
               * @return isValidProof Boolean indicating if the referral proof is valid.
               */
              function _processBonuses(
                  string calldata referralCode,
                  bytes32[] calldata referralMerkleProof,
                  uint256 tokensBought,
                  uint256 usdAmount,
                  address user
              ) private returns (uint256 bonusTokens, bool isValidProof) {
                  (bonusTokens, isValidProof) = _processReferralBonus(referralCode, referralMerkleProof, tokensBought, usdAmount, user);
                  bonusTokens += _processVolumeBuyBonus(tokensBought, usdAmount, user);
              }
              /**
               * @notice Processes the volume buy bonus for a given purchase.
               * @dev This function calculates and awards volume buy bonus tokens if the bonus is enabled.
               * @param tokensBought The number of tokens bought.
               * @param usdAmount The USD amount of the purchase.
               * @param user The address of the user making the purchase.
               * @return bonusTokens The number of bonus tokens awarded.
               */
              function _processVolumeBuyBonus(uint256 tokensBought, uint256 usdAmount, address user) private returns (uint256 bonusTokens) {
                  if (!volumeBuyBonusEnabled) {
                      return bonusTokens;
                  }
                  bonusTokens = _getVolumeBuyBonusTokens(tokensBought, usdAmount);
                  if (bonusTokens > 0) {
                      _awardBonusTokens(bonusTokens, user);
                      return bonusTokens;
                  } else {
                      return bonusTokens;
                  }
              }
              /**
               * @notice Calculates the volume buy bonus tokens based on the amount bought and USD amount.
               * @param tokensBought The number of tokens bought.
               * @param usdAmount The USD amount of the purchase.
               * @return bonusTokens The number of bonus tokens awarded.
               */
              function _getVolumeBuyBonusTokens(uint256 tokensBought, uint256 usdAmount) private view returns (uint256 bonusTokens) {
                  bonusTokens = 0;
                  for (uint i = uint(VolumeBuyBonusTier.TIER_1); i <= uint(VolumeBuyBonusTier.TIER_4);) {
                      VolumeBuyBonusTier tierIndex = VolumeBuyBonusTier(i);
                      if (usdAmount > volumeBuyBonusTiers[tierIndex].thresholdUsdAmount) {
                          bonusTokens = (tokensBought * volumeBuyBonusTiers[tierIndex].bonusPercentage) / _PERCENTAGE_DIVISOR;
                      } else {
                          break;
                      }
                      unchecked {
                          ++i;
                      }
                  }
              }
              /**
               * @notice Handles the purchase of tokens with native coins.
               * @dev This function processes the token purchase, sends funds to the treasury, and refunds any extra funds.
               * @param tokensBought The number of tokens bought.
               * @param cost The cost in native coins.
               * @param user The address of the user making the purchase.
               * @param bonusTokens The number of bonus tokens awarded.
               * @param usdAmount The USD amount of the purchase.
               * @param isValidProof Boolean indicating if the referral proof is valid.
               * @param referralCode The referral code used.
               * @param valueSent The amount of native coins sent by the user.
               */
              function _buyTokensWithNativeCoin(
                  uint256 tokensBought,
                  uint256 cost,
                  address user,
                  uint256 bonusTokens,
                  uint256 usdAmount,
                  bool isValidProof,
                  string calldata referralCode,
                  uint256 valueSent
              ) withNativeCoinEnabled private {
                  if (cost == 0) {
                      revert ZeroValueError();
                  }
                  uint256 amountReceived = valueSent;
                  tokensSold += tokensBought;
                  tokensInfoPerPhase[currentPresalePhaseIndex].tokensSold += tokensBought;
                  uint256 amountToSend = (amountReceived - cost > _RETURN_AMOUNT_THRESHOLD)
                      ? cost
                      : amountReceived;
                  (bool sent,) = payable(treasuryWallet).call{value: amountToSend}("");
                  if (!sent) {
                      revert FailedToSendToTreasuryError();
                  }
                  uint256 remainder = amountReceived - amountToSend;
                  if (remainder > 0) {
                      (sent,) = payable(user).call{value: remainder}("");
                      if (!sent) {
                          revert FailedToRefundExtraFundsError(user);
                      }
                  }
                  emit TokensBoughtEvent(
                      user,
                      address(0),
                      amountToSend,
                      tokensBought,
                      currentPresalePhaseIndex,
                      block.timestamp,
                      bonusTokens,
                      usdAmount,
                      isValidProof,
                      referralCode
                  );
              }
              /**
               * @notice Handles the purchase of tokens with ERC20 tokens.
               * @dev This function processes the token purchase and transfers funds to the treasury.
               * @param paymentToken The ERC20 token used for payment.
               * @param tokensBought The number of tokens bought.
               * @param cost The cost in ERC20 tokens.
               * @param user The address of the user making the purchase.
               * @param decimals The number of decimals for the ERC20 token.
               * @param bonusTokens The number of bonus tokens awarded.
               * @param usdAmount The USD amount of the purchase.
               * @param isValidProof Boolean indicating if the referral proof is valid.
               * @param referralCode The referral code used.
               */
              function _buyTokensWithERC20(
                  IERC20 paymentToken,
                  uint256 tokensBought,
                  uint256 cost,
                  address user,
                  uint8 decimals,
                  uint256 bonusTokens,
                  uint256 usdAmount,
                  bool isValidProof,
                  string calldata referralCode
              ) private {
                  tokensSold += tokensBought;
                  tokensInfoPerPhase[currentPresalePhaseIndex].tokensSold += tokensBought;
                  if (decimals < _BASE_DECIMALS) {
                      cost = cost / 10 ** (_BASE_DECIMALS - decimals);
                  }
                  if (cost == 0) {
                      revert ZeroValueError();
                  }
                  paymentToken.safeTransferFrom(user, treasuryWallet, cost);
                  emit TokensBoughtEvent(
                      user,
                      address(paymentToken),
                      cost,
                      tokensBought,
                      currentPresalePhaseIndex,
                      block.timestamp,
                      bonusTokens,
                      usdAmount,
                      isValidProof,
                      referralCode
                  );
              }
              /**
               * @notice Verifies if the given Merkle proof is valid for a user and amount.
               */
              function _isValidProof(address user, bytes32[] calldata merkleProof, uint256 amount) private view returns (bool isValid) {
                  bytes32 node = keccak256(abi.encodePacked(user, amount));
                  isValid = MerkleProof.verifyCalldata(
                      merkleProof,
                      claimingMerkleRoot,
                      node
                  );
              }
              /**
               * @notice Initializes roles for the contract.
               */
              function _initializeRoles(address serverAddress) private {
                  address user = msg.sender;
                  if (serverAddress == address(0)) {
                      revert ZeroAddressError();
                  }
                  _grantRole(DEFAULT_ADMIN_ROLE, user);
                  _grantRole(SERVER_ROLE, serverAddress);
                  emit RolesInitializedEvent(user, serverAddress);
              }
              /**
               * @notice Initializes the payment configuration with the native coin data feed.
               */
              function _initializePayment(
                  address _nativeCoinDataFeed,
                  uint256 _heartbeat
              ) private {
                  nativeCoinDataFeed = AggregatorV3Interface(_nativeCoinDataFeed);
                  nativeCoinDataFeedHeartbeat = _heartbeat;
                  emit PaymentInitializedEvent(_nativeCoinDataFeed, _heartbeat);
              }
          /**
           * @notice Initializes the treasury wallet address.
               * @dev This function sets up the treasury wallet. The treasury wallet address cannot be the zero address.
               * @param _treasuryWallet The address of the treasury wallet.
               */
              function _initializeTreasuryWallet(address _treasuryWallet) private {
                  if (_treasuryWallet == address(0)) {
                      revert ZeroAddressError();
                  }
                  treasuryWallet = _treasuryWallet;
                  emit TreasuryWalletInitializedEvent(_treasuryWallet);
              }
          /**
           * @notice Checks if the current claiming phase is the last phase.
               * @return True if the current claiming phase is the last phase, false otherwise.
               */
              function _isLastClaimingPhase() private view returns (bool) {
                  return claimingPhaseIndex == claimingPhases.length - 1;
              }
          /**
           * @notice Calculates the total claim amount for a user, including bought tokens, bonus tokens, and verified claim amount.
               * @param isValidProof Boolean indicating if the claim proof is valid.
               * @param amount The amount to claim based on the Merkle proof.
               * @param user The address of the user.
               * @return resultAmount The total amount the user is eligible to claim.
               */
              function _calculateTotalClaimAmount(bool isValidProof, uint256 amount, address user) private view returns (uint256 resultAmount) {
                  resultAmount += userToAmountTokensBought[user];
                  resultAmount += userToBonusTokensAcquired[user];
                  if (isValidProof) {
                      resultAmount += amount;
                  }
              }
          /**
           * @notice Updates the claiming phase index based on the current timestamp.
               * @dev This function advances the claiming phase index if the current timestamp has passed the end of the current phase.
               */
              function _updateClaimingPhaseIndex() private {
                  while (claimingPhaseIndex < claimingPhases.length - 1 &&
                      block.timestamp >= claimingPhases[claimingPhaseIndex].phaseEnd) {
                      claimingPhaseIndex++;
                  }
              }
          }// SPDX-License-Identifier: MIT
          pragma solidity 0.8.25;
          interface IPresale {
              event TokensBoughtEvent(
                  address indexed buyer,
                  address indexed paymentToken,
                  uint256 amountPaid,
                  uint256 amountOfTokensBought,
                  uint8 presalePhase,
                  uint256 timestamp,
                  uint256 bonusTokens,
                  uint256 usdAmount,
                  bool isValidProof,
                  string referralCode
              );
              event TokensBoughtWertEvent(
                  address indexed buyer,
                  uint256 amountPaid,
                  uint256 amountOfTokensBought,
                  uint8 presalePhase,
                  uint256 timestamp,
                  uint256 bonusTokens,
                  uint256 usdAmount,
                  bool isValidProof,
                  string referralCode
              );
              event BonusTokensEvent(
                  address indexed buyer,
                  uint256 amountOfBonusTokens,
                  uint256 timestamp,
                  uint8 presalePhase
              );
              event ReferrerBonusEvent(
                  uint256 referrerBonusUsdt,
                  uint256 timestamp,
                  uint8 presalePhase,
                  string referralCode
              );
              event ReferralBonusTierSetEvent(
                  ReferralBonusTier tier,
                  uint16 referrerPercentage,
                  uint16 referredPercentage,
                  uint128 thresholdUsdAmount
              );
              event ReferralBonusSetEvent(
                  bool awardOnlyFirstPurchase,
                  bool enabled
              );
              event VolumeBuyBonusEnabledEvent(
                  bool enabled
              );
              event VolumeBuyBonusTierSetEvent(
                  VolumeBuyBonusTier tier,
                  uint16 bonusPercentage,
                  uint128 thresholdUsdAmount
              );
              event PresaleStateChangedEvent(
                  PresaleState state
              );
              event ClaimingStateChangedEvent(
                  bool state
              );
              event ClaimingSetEvent(
                  uint256 claimingStartTimestamp,
                  uint256[] claimingPhaseVestingDurations,
                  uint16[] claimingPhasePercentages,
                  bytes32 claimingMerkleRoot
              );
              event PaymentTokenSetEvent(
                  address paymentToken,
                  bool enabled
              );
              event TreasuryWalletUpdatedEvent(
                  address treasuryWallet
              );
              event TokenDataFeedUpdatedEvent(
                  address tokenAddress,
                  address tokenDataFeed,
                  uint256 tokenDataFeedHeartbeat
              );
              event TokensWithdrawnEvent(
                  address indexed recipient,
                  uint256 amount
              );
              event NativeCoinWithdrawnEvent(
                  address indexed recipient,
                  uint256 amount
              );
              event ReferralMerkleRootUpdatedEvent(
                  bytes32 referralMerkleRoot
              );
              event ClaimingMerkleRootUpdatedEvent(
                  bytes32 claimingMerkleRoot
              );
              event HotWalletAddressSetEvent(
                  address hotWalletAddress
              );
              event EstimationAddressSetEvent(
                  address estimationAddress
              );
              event PresalePhaseSetEvent(
                  uint256 phaseIndex
              );
              event MinimumPurchaseUsdAmountSetEvent(
                  uint256 minimumPurchaseUsdAmount
              );
              event ClaimableTokenSetEvent(
                  address claimableToken
              );
              event BonusesInitializedEvent(
                  ReferralBonusTierInfo referralBonusTier1,
                  ReferralBonusTierInfo referralBonusTier2,
                  bool awardOnlyFirstPurchase,
                  bool referralBonusEnabled,
                  VolumeBuyBonusTierInfo volumeBuyBonusTier1,
                  VolumeBuyBonusTierInfo volumeBuyBonusTier2,
                  VolumeBuyBonusTierInfo volumeBuyBonusTier3,
                  VolumeBuyBonusTierInfo volumeBuyBonusTier4,
                  bool volumeBuyBonusEnabled
              );
              event RolesInitializedEvent(
                  address admin,
                  address server
              );
              event PaymentInitializedEvent(
                  address nativeCoinDataFeed,
                  uint256 nativeCoinDataFeedHeartbeats
              );
              event TreasuryWalletInitializedEvent(
                  address treasuryWallet
              );
              event PresaleInitializedEvent(
                  address _nativeCoinDataFeed,
                  address _treasuryWallet,
                  uint128[] _tokenPricesPerPhase,
                  address _tokenAddress,
                  address _hotWalletAddress,
                  address _estimationAddress,
                  uint256 _minimumPurchaseUsdAmount,
                  address _serverAddress,
                  bool _nativeCoinEnabled
              );
              event NativeCoinDataFeedUpdatedEvent(
                  address nativeCoinDataFeed,
                  uint256 nativeCoinDataFeedHeartbeat
              );
              event PresalePhaseStartTimestampSetEvent(
                  uint256 startTimestamp
              );
              error PresaleNotActiveError();
              error ZeroValueError();
              error InsufficientFundsError(uint256 required, uint256 sent);
              error FailedToSendToTreasuryError();
              error FailedToRefundExtraFundsError(address user);
              error InvalidExchangeRateError(uint256 rate);
              error UnsupportedERC20PaymentTokenError(address paymentToken);
              error InvalidSenderAddressError(address sender);
              error InvalidTokenRecipientAddressError(address recipient);
              error MinimumPurchaseError();
              error NativeCoinNotEnabledError();
              error ExceedsMaxPhasesError();
              error InvalidTokenDataFeedError(address tokenDataFeed);
              error WithdrawalFailedError();
              error InvalidDecimalsError();
              error InvalidPaymentTokenError(address paymentToken);
              error ZeroAddressError();
              error InvalidAddressError(address _address);
              error DataFeedHeartbeatError();
              error InvalidHeartbeatError();
              error PresalePhaseNotActiveError();
              error InvalidClaimingPercentagesError();
              function buyTokens(
                  string memory _referralCode,
                  bytes32[] calldata _merkleProofReferral,
                  uint256 _numberOfTokens,
                  address _paymentTokenAddress
              ) external payable;
              struct TokensInfoPerPhase {
                  uint256 tokensPrice;
                  uint256 tokensSold;
              }
              struct ReferralBonusTierInfo {
                  uint16 referrerPercentage;
                  uint16 referredPercentage;
                  uint128 thresholdUsdAmount;
              }
              struct ReferralBonusInfo {
                  bool awardOnlyFirstPurchase;
                  bool enabled;
              }
              struct VolumeBuyBonusInfo {
                  bool enabled;
              }
              struct VolumeBuyBonusTierInfo {
                  uint16 bonusPercentage;
                  uint128 thresholdUsdAmount;
              }
              enum ReferralBonusTier {
                  TIER_1,
                  TIER_2
              }
              enum VolumeBuyBonusTier {
                  TIER_1,
                  TIER_2,
                  TIER_3,
                  TIER_4
              }
              enum PresaleState {
                  COMING_SOON,
                  ACTIVE,
                  PAUSED,
                  FINISHED
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity 0.8.25;
          interface IClaiming {
              struct ClaimingPhase {
                  uint256 phaseEnd;
                  uint16 phasePercentage;
              }
              struct ClaimInfo {
                  uint256 claimedAmount;
                  uint256 phaseOfClaiming;
                  uint256 timestamp;
              }
              function claimTokens(bytes32[] calldata merkleProof, uint256 amount) external;
              error InvalidClaimingDataError();
              error ClaimingNotActiveError();
              error ClaimingNotSetError();
              error NoTokensToClaimError();
              event TokensClaimedEvent(address indexed user, uint256 amount);
          }// SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)
          pragma solidity ^0.8.20;
          /**
           * @dev Interface of the ERC-20 standard as defined in the ERC.
           */
          interface IERC20 {
              /**
               * @dev Emitted when `value` tokens are moved from one account (`from`) to
               * another (`to`).
               *
               * Note that `value` may be zero.
               */
              event Transfer(address indexed from, address indexed to, uint256 value);
              /**
               * @dev Emitted when the allowance of a `spender` for an `owner` is set by
               * a call to {approve}. `value` is the new allowance.
               */
              event Approval(address indexed owner, address indexed spender, uint256 value);
              /**
               * @dev Returns the value of tokens in existence.
               */
              function totalSupply() external view returns (uint256);
              /**
               * @dev Returns the value of tokens owned by `account`.
               */
              function balanceOf(address account) external view returns (uint256);
              /**
               * @dev Moves a `value` amount of tokens from the caller's account to `to`.
               *
               * Returns a boolean value indicating whether the operation succeeded.
               *
               * Emits a {Transfer} event.
               */
              function transfer(address to, uint256 value) external returns (bool);
              /**
               * @dev Returns the remaining number of tokens that `spender` will be
               * allowed to spend on behalf of `owner` through {transferFrom}. This is
               * zero by default.
               *
               * This value changes when {approve} or {transferFrom} are called.
               */
              function allowance(address owner, address spender) external view returns (uint256);
              /**
               * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
               * caller's tokens.
               *
               * Returns a boolean value indicating whether the operation succeeded.
               *
               * IMPORTANT: Beware that changing an allowance with this method brings the risk
               * that someone may use both the old and the new allowance by unfortunate
               * transaction ordering. One possible solution to mitigate this race
               * condition is to first reduce the spender's allowance to 0 and set the
               * desired value afterwards:
               * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
               *
               * Emits an {Approval} event.
               */
              function approve(address spender, uint256 value) external returns (bool);
              /**
               * @dev Moves a `value` amount of tokens from `from` to `to` using the
               * allowance mechanism. `value` is then deducted from the caller's
               * allowance.
               *
               * Returns a boolean value indicating whether the operation succeeded.
               *
               * Emits a {Transfer} event.
               */
              function transferFrom(address from, address to, uint256 value) external returns (bool);
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/ERC20.sol)
          pragma solidity ^0.8.20;
          import {IERC20} from "./IERC20.sol";
          import {IERC20Metadata} from "./extensions/IERC20Metadata.sol";
          import {Context} from "../../utils/Context.sol";
          import {IERC20Errors} from "../../interfaces/draft-IERC6093.sol";
          /**
           * @dev Implementation of the {IERC20} interface.
           *
           * This implementation is agnostic to the way tokens are created. This means
           * that a supply mechanism has to be added in a derived contract using {_mint}.
           *
           * TIP: For a detailed writeup see our guide
           * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
           * to implement supply mechanisms].
           *
           * The default value of {decimals} is 18. To change this, you should override
           * this function so it returns a different value.
           *
           * We have followed general OpenZeppelin Contracts guidelines: functions revert
           * instead returning `false` on failure. This behavior is nonetheless
           * conventional and does not conflict with the expectations of ERC-20
           * applications.
           */
          abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {
              mapping(address account => uint256) private _balances;
              mapping(address account => mapping(address spender => 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 returns (string memory) {
                  return _name;
              }
              /**
               * @dev Returns the symbol of the token, usually a shorter version of the
               * name.
               */
              function symbol() public view virtual 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 returns (uint8) {
                  return 18;
              }
              /**
               * @dev See {IERC20-totalSupply}.
               */
              function totalSupply() public view virtual returns (uint256) {
                  return _totalSupply;
              }
              /**
               * @dev See {IERC20-balanceOf}.
               */
              function balanceOf(address account) public view virtual 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 `value`.
               */
              function transfer(address to, uint256 value) public virtual returns (bool) {
                  address owner = _msgSender();
                  _transfer(owner, to, value);
                  return true;
              }
              /**
               * @dev See {IERC20-allowance}.
               */
              function allowance(address owner, address spender) public view virtual returns (uint256) {
                  return _allowances[owner][spender];
              }
              /**
               * @dev See {IERC20-approve}.
               *
               * NOTE: If `value` is the maximum `uint256`, the allowance is not updated on
               * `transferFrom`. This is semantically equivalent to an infinite approval.
               *
               * Requirements:
               *
               * - `spender` cannot be the zero address.
               */
              function approve(address spender, uint256 value) public virtual returns (bool) {
                  address owner = _msgSender();
                  _approve(owner, spender, value);
                  return true;
              }
              /**
               * @dev See {IERC20-transferFrom}.
               *
               * Skips emitting an {Approval} event indicating an allowance update. This is not
               * required by the ERC. See {xref-ERC20-_approve-address-address-uint256-bool-}[_approve].
               *
               * NOTE: Does not update the allowance if the current allowance
               * is the maximum `uint256`.
               *
               * Requirements:
               *
               * - `from` and `to` cannot be the zero address.
               * - `from` must have a balance of at least `value`.
               * - the caller must have allowance for ``from``'s tokens of at least
               * `value`.
               */
              function transferFrom(address from, address to, uint256 value) public virtual returns (bool) {
                  address spender = _msgSender();
                  _spendAllowance(from, spender, value);
                  _transfer(from, to, value);
                  return true;
              }
              /**
               * @dev Moves a `value` amount of tokens from `from` to `to`.
               *
               * This internal function is equivalent to {transfer}, and can be used to
               * e.g. implement automatic token fees, slashing mechanisms, etc.
               *
               * Emits a {Transfer} event.
               *
               * NOTE: This function is not virtual, {_update} should be overridden instead.
               */
              function _transfer(address from, address to, uint256 value) internal {
                  if (from == address(0)) {
                      revert ERC20InvalidSender(address(0));
                  }
                  if (to == address(0)) {
                      revert ERC20InvalidReceiver(address(0));
                  }
                  _update(from, to, value);
              }
              /**
               * @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from`
               * (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding
               * this function.
               *
               * Emits a {Transfer} event.
               */
              function _update(address from, address to, uint256 value) internal virtual {
                  if (from == address(0)) {
                      // Overflow check required: The rest of the code assumes that totalSupply never overflows
                      _totalSupply += value;
                  } else {
                      uint256 fromBalance = _balances[from];
                      if (fromBalance < value) {
                          revert ERC20InsufficientBalance(from, fromBalance, value);
                      }
                      unchecked {
                          // Overflow not possible: value <= fromBalance <= totalSupply.
                          _balances[from] = fromBalance - value;
                      }
                  }
                  if (to == address(0)) {
                      unchecked {
                          // Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply.
                          _totalSupply -= value;
                      }
                  } else {
                      unchecked {
                          // Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.
                          _balances[to] += value;
                      }
                  }
                  emit Transfer(from, to, value);
              }
              /**
               * @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0).
               * Relies on the `_update` mechanism
               *
               * Emits a {Transfer} event with `from` set to the zero address.
               *
               * NOTE: This function is not virtual, {_update} should be overridden instead.
               */
              function _mint(address account, uint256 value) internal {
                  if (account == address(0)) {
                      revert ERC20InvalidReceiver(address(0));
                  }
                  _update(address(0), account, value);
              }
              /**
               * @dev Destroys a `value` amount of tokens from `account`, lowering the total supply.
               * Relies on the `_update` mechanism.
               *
               * Emits a {Transfer} event with `to` set to the zero address.
               *
               * NOTE: This function is not virtual, {_update} should be overridden instead
               */
              function _burn(address account, uint256 value) internal {
                  if (account == address(0)) {
                      revert ERC20InvalidSender(address(0));
                  }
                  _update(account, address(0), value);
              }
              /**
               * @dev Sets `value` as the allowance of `spender` over the `owner` s tokens.
               *
               * This internal function is equivalent to `approve`, and can be used to
               * e.g. set automatic allowances for certain subsystems, etc.
               *
               * Emits an {Approval} event.
               *
               * Requirements:
               *
               * - `owner` cannot be the zero address.
               * - `spender` cannot be the zero address.
               *
               * Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
               */
              function _approve(address owner, address spender, uint256 value) internal {
                  _approve(owner, spender, value, true);
              }
              /**
               * @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event.
               *
               * By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by
               * `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any
               * `Approval` event during `transferFrom` operations.
               *
               * Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to
               * true using the following override:
               *
               * ```solidity
               * function _approve(address owner, address spender, uint256 value, bool) internal virtual override {
               *     super._approve(owner, spender, value, true);
               * }
               * ```
               *
               * Requirements are the same as {_approve}.
               */
              function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {
                  if (owner == address(0)) {
                      revert ERC20InvalidApprover(address(0));
                  }
                  if (spender == address(0)) {
                      revert ERC20InvalidSpender(address(0));
                  }
                  _allowances[owner][spender] = value;
                  if (emitEvent) {
                      emit Approval(owner, spender, value);
                  }
              }
              /**
               * @dev Updates `owner` s allowance for `spender` based on spent `value`.
               *
               * Does not update the allowance value in case of infinite allowance.
               * Revert if not enough allowance is available.
               *
               * Does not emit an {Approval} event.
               */
              function _spendAllowance(address owner, address spender, uint256 value) internal virtual {
                  uint256 currentAllowance = allowance(owner, spender);
                  if (currentAllowance != type(uint256).max) {
                      if (currentAllowance < value) {
                          revert ERC20InsufficientAllowance(spender, currentAllowance, value);
                      }
                      unchecked {
                          _approve(owner, spender, currentAllowance - value, false);
                      }
                  }
              }
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)
          pragma solidity ^0.8.20;
          import {IERC20} from "../IERC20.sol";
          import {IERC1363} from "../../../interfaces/IERC1363.sol";
          import {Address} from "../../../utils/Address.sol";
          /**
           * @title SafeERC20
           * @dev Wrappers around ERC-20 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 {
              /**
               * @dev An operation with an ERC-20 token failed.
               */
              error SafeERC20FailedOperation(address token);
              /**
               * @dev Indicates a failed `decreaseAllowance` request.
               */
              error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
              /**
               * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
               * non-reverting calls are assumed to be successful.
               */
              function safeTransfer(IERC20 token, address to, uint256 value) internal {
                  _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
              }
              /**
               * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
               * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
               */
              function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
                  _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
              }
              /**
               * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
               * non-reverting calls are assumed to be successful.
               *
               * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
               * smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
               * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
               * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
               */
              function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
                  uint256 oldAllowance = token.allowance(address(this), spender);
                  forceApprove(token, spender, oldAllowance + value);
              }
              /**
               * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
               * value, non-reverting calls are assumed to be successful.
               *
               * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
               * smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
               * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
               * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
               */
              function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
                  unchecked {
                      uint256 currentAllowance = token.allowance(address(this), spender);
                      if (currentAllowance < requestedDecrease) {
                          revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
                      }
                      forceApprove(token, spender, currentAllowance - requestedDecrease);
                  }
              }
              /**
               * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
               * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
               * to be set to zero before setting it to a non-zero value, such as USDT.
               *
               * NOTE: If the token implements ERC-7674, this function will not modify any temporary allowance. This function
               * only sets the "standard" allowance. Any temporary allowance will remain active, in addition to the value being
               * set here.
               */
              function forceApprove(IERC20 token, address spender, uint256 value) internal {
                  bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
                  if (!_callOptionalReturnBool(token, approvalCall)) {
                      _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
                      _callOptionalReturn(token, approvalCall);
                  }
              }
              /**
               * @dev Performs an {ERC1363} transferAndCall, with a fallback to the simple {ERC20} transfer if the target has no
               * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
               * targeting contracts.
               *
               * Reverts if the returned value is other than `true`.
               */
              function transferAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
                  if (to.code.length == 0) {
                      safeTransfer(token, to, value);
                  } else if (!token.transferAndCall(to, value, data)) {
                      revert SafeERC20FailedOperation(address(token));
                  }
              }
              /**
               * @dev Performs an {ERC1363} transferFromAndCall, with a fallback to the simple {ERC20} transferFrom if the target
               * has no code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
               * targeting contracts.
               *
               * Reverts if the returned value is other than `true`.
               */
              function transferFromAndCallRelaxed(
                  IERC1363 token,
                  address from,
                  address to,
                  uint256 value,
                  bytes memory data
              ) internal {
                  if (to.code.length == 0) {
                      safeTransferFrom(token, from, to, value);
                  } else if (!token.transferFromAndCall(from, to, value, data)) {
                      revert SafeERC20FailedOperation(address(token));
                  }
              }
              /**
               * @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no
               * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
               * targeting contracts.
               *
               * NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}.
               * Opposedly, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall}
               * once without retrying, and relies on the returned value to be true.
               *
               * Reverts if the returned value is other than `true`.
               */
              function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
                  if (to.code.length == 0) {
                      forceApprove(token, to, value);
                  } else if (!token.approveAndCall(to, value, data)) {
                      revert SafeERC20FailedOperation(address(token));
                  }
              }
              /**
               * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
               * on the return value: the return value is optional (but if data is returned, it must not be false).
               * @param token The token targeted by the call.
               * @param data The call data (encoded using abi.encode or one of its variants).
               *
               * This is a variant of {_callOptionalReturnBool} that reverts if call fails to meet the requirements.
               */
              function _callOptionalReturn(IERC20 token, bytes memory data) private {
                  uint256 returnSize;
                  uint256 returnValue;
                  assembly ("memory-safe") {
                      let success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
                      // bubble errors
                      if iszero(success) {
                          let ptr := mload(0x40)
                          returndatacopy(ptr, 0, returndatasize())
                          revert(ptr, returndatasize())
                      }
                      returnSize := returndatasize()
                      returnValue := mload(0)
                  }
                  if (returnSize == 0 ? address(token).code.length == 0 : returnValue != 1) {
                      revert SafeERC20FailedOperation(address(token));
                  }
              }
              /**
               * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
               * on the return value: the return value is optional (but if data is returned, it must not be false).
               * @param token The token targeted by the call.
               * @param data The call data (encoded using abi.encode or one of its variants).
               *
               * This is a variant of {_callOptionalReturn} that silently catches all reverts and returns a bool instead.
               */
              function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
                  bool success;
                  uint256 returnSize;
                  uint256 returnValue;
                  assembly ("memory-safe") {
                      success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
                      returnSize := returndatasize()
                      returnValue := mload(0)
                  }
                  return success && (returnSize == 0 ? address(token).code.length > 0 : returnValue == 1);
              }
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v5.1.0) (proxy/utils/UUPSUpgradeable.sol)
          pragma solidity ^0.8.20;
          import {IERC1822Proxiable} from "@openzeppelin/contracts/interfaces/draft-IERC1822.sol";
          import {ERC1967Utils} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol";
          import {Initializable} from "./Initializable.sol";
          /**
           * @dev An upgradeability mechanism designed for UUPS proxies. The functions included here can perform an upgrade of an
           * {ERC1967Proxy}, when this contract is set as the implementation behind such a proxy.
           *
           * A security mechanism ensures that an upgrade does not turn off upgradeability accidentally, although this risk is
           * reinstated if the upgrade retains upgradeability but removes the security mechanism, e.g. by replacing
           * `UUPSUpgradeable` with a custom implementation of upgrades.
           *
           * The {_authorizeUpgrade} function must be overridden to include access restriction to the upgrade mechanism.
           */
          abstract contract UUPSUpgradeable is Initializable, IERC1822Proxiable {
              /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
              address private immutable __self = address(this);
              /**
               * @dev The version of the upgrade interface of the contract. If this getter is missing, both `upgradeTo(address)`
               * and `upgradeToAndCall(address,bytes)` are present, and `upgradeTo` must be used if no function should be called,
               * while `upgradeToAndCall` will invoke the `receive` function if the second argument is the empty byte string.
               * If the getter returns `"5.0.0"`, only `upgradeToAndCall(address,bytes)` is present, and the second argument must
               * be the empty byte string if no function should be called, making it impossible to invoke the `receive` function
               * during an upgrade.
               */
              string public constant UPGRADE_INTERFACE_VERSION = "5.0.0";
              /**
               * @dev The call is from an unauthorized context.
               */
              error UUPSUnauthorizedCallContext();
              /**
               * @dev The storage `slot` is unsupported as a UUID.
               */
              error UUPSUnsupportedProxiableUUID(bytes32 slot);
              /**
               * @dev Check that the execution is being performed through a delegatecall call and that the execution context is
               * a proxy contract with an implementation (as defined in ERC-1967) pointing to self. This should only be the case
               * for UUPS and transparent proxies that are using the current contract as their implementation. Execution of a
               * function through ERC-1167 minimal proxies (clones) would not normally pass this test, but is not guaranteed to
               * fail.
               */
              modifier onlyProxy() {
                  _checkProxy();
                  _;
              }
              /**
               * @dev Check that the execution is not being performed through a delegate call. This allows a function to be
               * callable on the implementing contract but not through proxies.
               */
              modifier notDelegated() {
                  _checkNotDelegated();
                  _;
              }
              function __UUPSUpgradeable_init() internal onlyInitializing {
              }
              function __UUPSUpgradeable_init_unchained() internal onlyInitializing {
              }
              /**
               * @dev Implementation of the ERC-1822 {proxiableUUID} function. This returns the storage slot used by the
               * implementation. It is used to validate the implementation's compatibility when performing an upgrade.
               *
               * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
               * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
               * function revert if invoked through a proxy. This is guaranteed by the `notDelegated` modifier.
               */
              function proxiableUUID() external view virtual notDelegated returns (bytes32) {
                  return ERC1967Utils.IMPLEMENTATION_SLOT;
              }
              /**
               * @dev Upgrade the implementation of the proxy to `newImplementation`, and subsequently execute the function call
               * encoded in `data`.
               *
               * Calls {_authorizeUpgrade}.
               *
               * Emits an {Upgraded} event.
               *
               * @custom:oz-upgrades-unsafe-allow-reachable delegatecall
               */
              function upgradeToAndCall(address newImplementation, bytes memory data) public payable virtual onlyProxy {
                  _authorizeUpgrade(newImplementation);
                  _upgradeToAndCallUUPS(newImplementation, data);
              }
              /**
               * @dev Reverts if the execution is not performed via delegatecall or the execution
               * context is not of a proxy with an ERC-1967 compliant implementation pointing to self.
               * See {_onlyProxy}.
               */
              function _checkProxy() internal view virtual {
                  if (
                      address(this) == __self || // Must be called through delegatecall
                      ERC1967Utils.getImplementation() != __self // Must be called through an active proxy
                  ) {
                      revert UUPSUnauthorizedCallContext();
                  }
              }
              /**
               * @dev Reverts if the execution is performed via delegatecall.
               * See {notDelegated}.
               */
              function _checkNotDelegated() internal view virtual {
                  if (address(this) != __self) {
                      // Must not be called through delegatecall
                      revert UUPSUnauthorizedCallContext();
                  }
              }
              /**
               * @dev Function that should revert when `msg.sender` is not authorized to upgrade the contract. Called by
               * {upgradeToAndCall}.
               *
               * Normally, this function will use an xref:access.adoc[access control] modifier such as {Ownable-onlyOwner}.
               *
               * ```solidity
               * function _authorizeUpgrade(address) internal onlyOwner {}
               * ```
               */
              function _authorizeUpgrade(address newImplementation) internal virtual;
              /**
               * @dev Performs an implementation upgrade with a security check for UUPS proxies, and additional setup call.
               *
               * As a security check, {proxiableUUID} is invoked in the new implementation, and the return value
               * is expected to be the implementation slot in ERC-1967.
               *
               * Emits an {IERC1967-Upgraded} event.
               */
              function _upgradeToAndCallUUPS(address newImplementation, bytes memory data) private {
                  try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) {
                      if (slot != ERC1967Utils.IMPLEMENTATION_SLOT) {
                          revert UUPSUnsupportedProxiableUUID(slot);
                      }
                      ERC1967Utils.upgradeToAndCall(newImplementation, data);
                  } catch {
                      // The implementation is not UUPS
                      revert ERC1967Utils.ERC1967InvalidImplementation(newImplementation);
                  }
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.0;
          // solhint-disable-next-line interface-starts-with-i
          interface AggregatorV3Interface {
            function decimals() external view returns (uint8);
            function description() external view returns (string memory);
            function version() external view returns (uint256);
            function getRoundData(
              uint80 _roundId
            ) external view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
            function latestRoundData()
              external
              view
              returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/MerkleProof.sol)
          // This file was procedurally generated from scripts/generate/templates/MerkleProof.js.
          pragma solidity ^0.8.20;
          import {Hashes} from "./Hashes.sol";
          /**
           * @dev These functions deal with verification of Merkle Tree proofs.
           *
           * The tree and the proofs can be generated using our
           * https://github.com/OpenZeppelin/merkle-tree[JavaScript library].
           * You will find a quickstart guide in the readme.
           *
           * WARNING: You should avoid using leaf values that are 64 bytes long prior to
           * hashing, or use a hash function other than keccak256 for hashing leaves.
           * This is because the concatenation of a sorted pair of internal nodes in
           * the Merkle tree could be reinterpreted as a leaf value.
           * OpenZeppelin's JavaScript library generates Merkle trees that are safe
           * against this attack out of the box.
           *
           * IMPORTANT: Consider memory side-effects when using custom hashing functions
           * that access memory in an unsafe way.
           *
           * NOTE: This library supports proof verification for merkle trees built using
           * custom _commutative_ hashing functions (i.e. `H(a, b) == H(b, a)`). Proving
           * leaf inclusion in trees built using non-commutative hashing functions requires
           * additional logic that is not supported by this library.
           */
          library MerkleProof {
              /**
               *@dev The multiproof provided is not valid.
               */
              error MerkleProofInvalidMultiproof();
              /**
               * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
               * defined by `root`. For this, a `proof` must be provided, containing
               * sibling hashes on the branch from the leaf to the root of the tree. Each
               * pair of leaves and each pair of pre-images are assumed to be sorted.
               *
               * This version handles proofs in memory with the default hashing function.
               */
              function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf) internal pure returns (bool) {
                  return processProof(proof, leaf) == root;
              }
              /**
               * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up
               * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
               * hash matches the root of the tree. When processing the proof, the pairs
               * of leaves & pre-images are assumed to be sorted.
               *
               * This version handles proofs in memory with the default hashing function.
               */
              function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) {
                  bytes32 computedHash = leaf;
                  for (uint256 i = 0; i < proof.length; i++) {
                      computedHash = Hashes.commutativeKeccak256(computedHash, proof[i]);
                  }
                  return computedHash;
              }
              /**
               * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
               * defined by `root`. For this, a `proof` must be provided, containing
               * sibling hashes on the branch from the leaf to the root of the tree. Each
               * pair of leaves and each pair of pre-images are assumed to be sorted.
               *
               * This version handles proofs in memory with a custom hashing function.
               */
              function verify(
                  bytes32[] memory proof,
                  bytes32 root,
                  bytes32 leaf,
                  function(bytes32, bytes32) view returns (bytes32) hasher
              ) internal view returns (bool) {
                  return processProof(proof, leaf, hasher) == root;
              }
              /**
               * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up
               * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
               * hash matches the root of the tree. When processing the proof, the pairs
               * of leaves & pre-images are assumed to be sorted.
               *
               * This version handles proofs in memory with a custom hashing function.
               */
              function processProof(
                  bytes32[] memory proof,
                  bytes32 leaf,
                  function(bytes32, bytes32) view returns (bytes32) hasher
              ) internal view returns (bytes32) {
                  bytes32 computedHash = leaf;
                  for (uint256 i = 0; i < proof.length; i++) {
                      computedHash = hasher(computedHash, proof[i]);
                  }
                  return computedHash;
              }
              /**
               * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
               * defined by `root`. For this, a `proof` must be provided, containing
               * sibling hashes on the branch from the leaf to the root of the tree. Each
               * pair of leaves and each pair of pre-images are assumed to be sorted.
               *
               * This version handles proofs in calldata with the default hashing function.
               */
              function verifyCalldata(bytes32[] calldata proof, bytes32 root, bytes32 leaf) internal pure returns (bool) {
                  return processProofCalldata(proof, leaf) == root;
              }
              /**
               * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up
               * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
               * hash matches the root of the tree. When processing the proof, the pairs
               * of leaves & pre-images are assumed to be sorted.
               *
               * This version handles proofs in calldata with the default hashing function.
               */
              function processProofCalldata(bytes32[] calldata proof, bytes32 leaf) internal pure returns (bytes32) {
                  bytes32 computedHash = leaf;
                  for (uint256 i = 0; i < proof.length; i++) {
                      computedHash = Hashes.commutativeKeccak256(computedHash, proof[i]);
                  }
                  return computedHash;
              }
              /**
               * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
               * defined by `root`. For this, a `proof` must be provided, containing
               * sibling hashes on the branch from the leaf to the root of the tree. Each
               * pair of leaves and each pair of pre-images are assumed to be sorted.
               *
               * This version handles proofs in calldata with a custom hashing function.
               */
              function verifyCalldata(
                  bytes32[] calldata proof,
                  bytes32 root,
                  bytes32 leaf,
                  function(bytes32, bytes32) view returns (bytes32) hasher
              ) internal view returns (bool) {
                  return processProofCalldata(proof, leaf, hasher) == root;
              }
              /**
               * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up
               * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
               * hash matches the root of the tree. When processing the proof, the pairs
               * of leaves & pre-images are assumed to be sorted.
               *
               * This version handles proofs in calldata with a custom hashing function.
               */
              function processProofCalldata(
                  bytes32[] calldata proof,
                  bytes32 leaf,
                  function(bytes32, bytes32) view returns (bytes32) hasher
              ) internal view returns (bytes32) {
                  bytes32 computedHash = leaf;
                  for (uint256 i = 0; i < proof.length; i++) {
                      computedHash = hasher(computedHash, proof[i]);
                  }
                  return computedHash;
              }
              /**
               * @dev Returns true if the `leaves` can be simultaneously proven to be a part of a Merkle tree defined by
               * `root`, according to `proof` and `proofFlags` as described in {processMultiProof}.
               *
               * This version handles multiproofs in memory with the default hashing function.
               *
               * CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
               *
               * NOTE: Consider the case where `root == proof[0] && leaves.length == 0` as it will return `true`.
               * The `leaves` must be validated independently. See {processMultiProof}.
               */
              function multiProofVerify(
                  bytes32[] memory proof,
                  bool[] memory proofFlags,
                  bytes32 root,
                  bytes32[] memory leaves
              ) internal pure returns (bool) {
                  return processMultiProof(proof, proofFlags, leaves) == root;
              }
              /**
               * @dev Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The reconstruction
               * proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another
               * leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false
               * respectively.
               *
               * This version handles multiproofs in memory with the default hashing function.
               *
               * CAUTION: Not all Merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree
               * is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the
               * tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer).
               *
               * NOTE: The _empty set_ (i.e. the case where `proof.length == 1 && leaves.length == 0`) is considered a no-op,
               * and therefore a valid multiproof (i.e. it returns `proof[0]`). Consider disallowing this case if you're not
               * validating the leaves elsewhere.
               */
              function processMultiProof(
                  bytes32[] memory proof,
                  bool[] memory proofFlags,
                  bytes32[] memory leaves
              ) internal pure returns (bytes32 merkleRoot) {
                  // This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by
                  // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
                  // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
                  // the Merkle tree.
                  uint256 leavesLen = leaves.length;
                  uint256 proofFlagsLen = proofFlags.length;
                  // Check proof validity.
                  if (leavesLen + proof.length != proofFlagsLen + 1) {
                      revert MerkleProofInvalidMultiproof();
                  }
                  // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
                  // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
                  bytes32[] memory hashes = new bytes32[](proofFlagsLen);
                  uint256 leafPos = 0;
                  uint256 hashPos = 0;
                  uint256 proofPos = 0;
                  // At each step, we compute the next hash using two values:
                  // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
                  //   get the next hash.
                  // - depending on the flag, either another value from the "main queue" (merging branches) or an element from the
                  //   `proof` array.
                  for (uint256 i = 0; i < proofFlagsLen; i++) {
                      bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
                      bytes32 b = proofFlags[i]
                          ? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
                          : proof[proofPos++];
                      hashes[i] = Hashes.commutativeKeccak256(a, b);
                  }
                  if (proofFlagsLen > 0) {
                      if (proofPos != proof.length) {
                          revert MerkleProofInvalidMultiproof();
                      }
                      unchecked {
                          return hashes[proofFlagsLen - 1];
                      }
                  } else if (leavesLen > 0) {
                      return leaves[0];
                  } else {
                      return proof[0];
                  }
              }
              /**
               * @dev Returns true if the `leaves` can be simultaneously proven to be a part of a Merkle tree defined by
               * `root`, according to `proof` and `proofFlags` as described in {processMultiProof}.
               *
               * This version handles multiproofs in memory with a custom hashing function.
               *
               * CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
               *
               * NOTE: Consider the case where `root == proof[0] && leaves.length == 0` as it will return `true`.
               * The `leaves` must be validated independently. See {processMultiProof}.
               */
              function multiProofVerify(
                  bytes32[] memory proof,
                  bool[] memory proofFlags,
                  bytes32 root,
                  bytes32[] memory leaves,
                  function(bytes32, bytes32) view returns (bytes32) hasher
              ) internal view returns (bool) {
                  return processMultiProof(proof, proofFlags, leaves, hasher) == root;
              }
              /**
               * @dev Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The reconstruction
               * proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another
               * leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false
               * respectively.
               *
               * This version handles multiproofs in memory with a custom hashing function.
               *
               * CAUTION: Not all Merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree
               * is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the
               * tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer).
               *
               * NOTE: The _empty set_ (i.e. the case where `proof.length == 1 && leaves.length == 0`) is considered a no-op,
               * and therefore a valid multiproof (i.e. it returns `proof[0]`). Consider disallowing this case if you're not
               * validating the leaves elsewhere.
               */
              function processMultiProof(
                  bytes32[] memory proof,
                  bool[] memory proofFlags,
                  bytes32[] memory leaves,
                  function(bytes32, bytes32) view returns (bytes32) hasher
              ) internal view returns (bytes32 merkleRoot) {
                  // This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by
                  // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
                  // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
                  // the Merkle tree.
                  uint256 leavesLen = leaves.length;
                  uint256 proofFlagsLen = proofFlags.length;
                  // Check proof validity.
                  if (leavesLen + proof.length != proofFlagsLen + 1) {
                      revert MerkleProofInvalidMultiproof();
                  }
                  // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
                  // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
                  bytes32[] memory hashes = new bytes32[](proofFlagsLen);
                  uint256 leafPos = 0;
                  uint256 hashPos = 0;
                  uint256 proofPos = 0;
                  // At each step, we compute the next hash using two values:
                  // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
                  //   get the next hash.
                  // - depending on the flag, either another value from the "main queue" (merging branches) or an element from the
                  //   `proof` array.
                  for (uint256 i = 0; i < proofFlagsLen; i++) {
                      bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
                      bytes32 b = proofFlags[i]
                          ? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
                          : proof[proofPos++];
                      hashes[i] = hasher(a, b);
                  }
                  if (proofFlagsLen > 0) {
                      if (proofPos != proof.length) {
                          revert MerkleProofInvalidMultiproof();
                      }
                      unchecked {
                          return hashes[proofFlagsLen - 1];
                      }
                  } else if (leavesLen > 0) {
                      return leaves[0];
                  } else {
                      return proof[0];
                  }
              }
              /**
               * @dev Returns true if the `leaves` can be simultaneously proven to be a part of a Merkle tree defined by
               * `root`, according to `proof` and `proofFlags` as described in {processMultiProof}.
               *
               * This version handles multiproofs in calldata with the default hashing function.
               *
               * CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
               *
               * NOTE: Consider the case where `root == proof[0] && leaves.length == 0` as it will return `true`.
               * The `leaves` must be validated independently. See {processMultiProofCalldata}.
               */
              function multiProofVerifyCalldata(
                  bytes32[] calldata proof,
                  bool[] calldata proofFlags,
                  bytes32 root,
                  bytes32[] memory leaves
              ) internal pure returns (bool) {
                  return processMultiProofCalldata(proof, proofFlags, leaves) == root;
              }
              /**
               * @dev Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The reconstruction
               * proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another
               * leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false
               * respectively.
               *
               * This version handles multiproofs in calldata with the default hashing function.
               *
               * CAUTION: Not all Merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree
               * is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the
               * tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer).
               *
               * NOTE: The _empty set_ (i.e. the case where `proof.length == 1 && leaves.length == 0`) is considered a no-op,
               * and therefore a valid multiproof (i.e. it returns `proof[0]`). Consider disallowing this case if you're not
               * validating the leaves elsewhere.
               */
              function processMultiProofCalldata(
                  bytes32[] calldata proof,
                  bool[] calldata proofFlags,
                  bytes32[] memory leaves
              ) internal pure returns (bytes32 merkleRoot) {
                  // This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by
                  // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
                  // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
                  // the Merkle tree.
                  uint256 leavesLen = leaves.length;
                  uint256 proofFlagsLen = proofFlags.length;
                  // Check proof validity.
                  if (leavesLen + proof.length != proofFlagsLen + 1) {
                      revert MerkleProofInvalidMultiproof();
                  }
                  // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
                  // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
                  bytes32[] memory hashes = new bytes32[](proofFlagsLen);
                  uint256 leafPos = 0;
                  uint256 hashPos = 0;
                  uint256 proofPos = 0;
                  // At each step, we compute the next hash using two values:
                  // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
                  //   get the next hash.
                  // - depending on the flag, either another value from the "main queue" (merging branches) or an element from the
                  //   `proof` array.
                  for (uint256 i = 0; i < proofFlagsLen; i++) {
                      bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
                      bytes32 b = proofFlags[i]
                          ? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
                          : proof[proofPos++];
                      hashes[i] = Hashes.commutativeKeccak256(a, b);
                  }
                  if (proofFlagsLen > 0) {
                      if (proofPos != proof.length) {
                          revert MerkleProofInvalidMultiproof();
                      }
                      unchecked {
                          return hashes[proofFlagsLen - 1];
                      }
                  } else if (leavesLen > 0) {
                      return leaves[0];
                  } else {
                      return proof[0];
                  }
              }
              /**
               * @dev Returns true if the `leaves` can be simultaneously proven to be a part of a Merkle tree defined by
               * `root`, according to `proof` and `proofFlags` as described in {processMultiProof}.
               *
               * This version handles multiproofs in calldata with a custom hashing function.
               *
               * CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
               *
               * NOTE: Consider the case where `root == proof[0] && leaves.length == 0` as it will return `true`.
               * The `leaves` must be validated independently. See {processMultiProofCalldata}.
               */
              function multiProofVerifyCalldata(
                  bytes32[] calldata proof,
                  bool[] calldata proofFlags,
                  bytes32 root,
                  bytes32[] memory leaves,
                  function(bytes32, bytes32) view returns (bytes32) hasher
              ) internal view returns (bool) {
                  return processMultiProofCalldata(proof, proofFlags, leaves, hasher) == root;
              }
              /**
               * @dev Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The reconstruction
               * proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another
               * leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false
               * respectively.
               *
               * This version handles multiproofs in calldata with a custom hashing function.
               *
               * CAUTION: Not all Merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree
               * is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the
               * tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer).
               *
               * NOTE: The _empty set_ (i.e. the case where `proof.length == 1 && leaves.length == 0`) is considered a no-op,
               * and therefore a valid multiproof (i.e. it returns `proof[0]`). Consider disallowing this case if you're not
               * validating the leaves elsewhere.
               */
              function processMultiProofCalldata(
                  bytes32[] calldata proof,
                  bool[] calldata proofFlags,
                  bytes32[] memory leaves,
                  function(bytes32, bytes32) view returns (bytes32) hasher
              ) internal view returns (bytes32 merkleRoot) {
                  // This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by
                  // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
                  // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
                  // the Merkle tree.
                  uint256 leavesLen = leaves.length;
                  uint256 proofFlagsLen = proofFlags.length;
                  // Check proof validity.
                  if (leavesLen + proof.length != proofFlagsLen + 1) {
                      revert MerkleProofInvalidMultiproof();
                  }
                  // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
                  // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
                  bytes32[] memory hashes = new bytes32[](proofFlagsLen);
                  uint256 leafPos = 0;
                  uint256 hashPos = 0;
                  uint256 proofPos = 0;
                  // At each step, we compute the next hash using two values:
                  // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
                  //   get the next hash.
                  // - depending on the flag, either another value from the "main queue" (merging branches) or an element from the
                  //   `proof` array.
                  for (uint256 i = 0; i < proofFlagsLen; i++) {
                      bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
                      bytes32 b = proofFlags[i]
                          ? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
                          : proof[proofPos++];
                      hashes[i] = hasher(a, b);
                  }
                  if (proofFlagsLen > 0) {
                      if (proofPos != proof.length) {
                          revert MerkleProofInvalidMultiproof();
                      }
                      unchecked {
                          return hashes[proofFlagsLen - 1];
                      }
                  } else if (leavesLen > 0) {
                      return leaves[0];
                  } else {
                      return proof[0];
                  }
              }
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v5.0.0) (access/AccessControl.sol)
          pragma solidity ^0.8.20;
          import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol";
          import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol";
          import {ERC165Upgradeable} from "../utils/introspection/ERC165Upgradeable.sol";
          import {Initializable} from "../proxy/utils/Initializable.sol";
          /**
           * @dev Contract module that allows children to implement role-based access
           * control mechanisms. This is a lightweight version that doesn't allow enumerating role
           * members except through off-chain means by accessing the contract event logs. Some
           * applications may benefit from on-chain enumerability, for those cases see
           * {AccessControlEnumerable}.
           *
           * Roles are referred to by their `bytes32` identifier. These should be exposed
           * in the external API and be unique. The best way to achieve this is by
           * using `public constant` hash digests:
           *
           * ```solidity
           * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
           * ```
           *
           * Roles can be used to represent a set of permissions. To restrict access to a
           * function call, use {hasRole}:
           *
           * ```solidity
           * function foo() public {
           *     require(hasRole(MY_ROLE, msg.sender));
           *     ...
           * }
           * ```
           *
           * Roles can be granted and revoked dynamically via the {grantRole} and
           * {revokeRole} functions. Each role has an associated admin role, and only
           * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
           *
           * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
           * that only accounts with this role will be able to grant or revoke other
           * roles. More complex role relationships can be created by using
           * {_setRoleAdmin}.
           *
           * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
           * grant and revoke this role. Extra precautions should be taken to secure
           * accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
           * to enforce additional security measures for this role.
           */
          abstract contract AccessControlUpgradeable is Initializable, ContextUpgradeable, IAccessControl, ERC165Upgradeable {
              struct RoleData {
                  mapping(address account => bool) hasRole;
                  bytes32 adminRole;
              }
              bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
              /// @custom:storage-location erc7201:openzeppelin.storage.AccessControl
              struct AccessControlStorage {
                  mapping(bytes32 role => RoleData) _roles;
              }
              // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.AccessControl")) - 1)) & ~bytes32(uint256(0xff))
              bytes32 private constant AccessControlStorageLocation = 0x02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800;
              function _getAccessControlStorage() private pure returns (AccessControlStorage storage $) {
                  assembly {
                      $.slot := AccessControlStorageLocation
                  }
              }
              /**
               * @dev Modifier that checks that an account has a specific role. Reverts
               * with an {AccessControlUnauthorizedAccount} error including the required role.
               */
              modifier onlyRole(bytes32 role) {
                  _checkRole(role);
                  _;
              }
              function __AccessControl_init() internal onlyInitializing {
              }
              function __AccessControl_init_unchained() internal onlyInitializing {
              }
              /**
               * @dev See {IERC165-supportsInterface}.
               */
              function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
                  return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
              }
              /**
               * @dev Returns `true` if `account` has been granted `role`.
               */
              function hasRole(bytes32 role, address account) public view virtual returns (bool) {
                  AccessControlStorage storage $ = _getAccessControlStorage();
                  return $._roles[role].hasRole[account];
              }
              /**
               * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()`
               * is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier.
               */
              function _checkRole(bytes32 role) internal view virtual {
                  _checkRole(role, _msgSender());
              }
              /**
               * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`
               * is missing `role`.
               */
              function _checkRole(bytes32 role, address account) internal view virtual {
                  if (!hasRole(role, account)) {
                      revert AccessControlUnauthorizedAccount(account, role);
                  }
              }
              /**
               * @dev Returns the admin role that controls `role`. See {grantRole} and
               * {revokeRole}.
               *
               * To change a role's admin, use {_setRoleAdmin}.
               */
              function getRoleAdmin(bytes32 role) public view virtual returns (bytes32) {
                  AccessControlStorage storage $ = _getAccessControlStorage();
                  return $._roles[role].adminRole;
              }
              /**
               * @dev Grants `role` to `account`.
               *
               * If `account` had not been already granted `role`, emits a {RoleGranted}
               * event.
               *
               * Requirements:
               *
               * - the caller must have ``role``'s admin role.
               *
               * May emit a {RoleGranted} event.
               */
              function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
                  _grantRole(role, account);
              }
              /**
               * @dev Revokes `role` from `account`.
               *
               * If `account` had been granted `role`, emits a {RoleRevoked} event.
               *
               * Requirements:
               *
               * - the caller must have ``role``'s admin role.
               *
               * May emit a {RoleRevoked} event.
               */
              function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
                  _revokeRole(role, account);
              }
              /**
               * @dev Revokes `role` from the calling account.
               *
               * Roles are often managed via {grantRole} and {revokeRole}: this function's
               * purpose is to provide a mechanism for accounts to lose their privileges
               * if they are compromised (such as when a trusted device is misplaced).
               *
               * If the calling account had been revoked `role`, emits a {RoleRevoked}
               * event.
               *
               * Requirements:
               *
               * - the caller must be `callerConfirmation`.
               *
               * May emit a {RoleRevoked} event.
               */
              function renounceRole(bytes32 role, address callerConfirmation) public virtual {
                  if (callerConfirmation != _msgSender()) {
                      revert AccessControlBadConfirmation();
                  }
                  _revokeRole(role, callerConfirmation);
              }
              /**
               * @dev Sets `adminRole` as ``role``'s admin role.
               *
               * Emits a {RoleAdminChanged} event.
               */
              function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
                  AccessControlStorage storage $ = _getAccessControlStorage();
                  bytes32 previousAdminRole = getRoleAdmin(role);
                  $._roles[role].adminRole = adminRole;
                  emit RoleAdminChanged(role, previousAdminRole, adminRole);
              }
              /**
               * @dev Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.
               *
               * Internal function without access restriction.
               *
               * May emit a {RoleGranted} event.
               */
              function _grantRole(bytes32 role, address account) internal virtual returns (bool) {
                  AccessControlStorage storage $ = _getAccessControlStorage();
                  if (!hasRole(role, account)) {
                      $._roles[role].hasRole[account] = true;
                      emit RoleGranted(role, account, _msgSender());
                      return true;
                  } else {
                      return false;
                  }
              }
              /**
               * @dev Attempts to revoke `role` to `account` and returns a boolean indicating if `role` was revoked.
               *
               * Internal function without access restriction.
               *
               * May emit a {RoleRevoked} event.
               */
              function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {
                  AccessControlStorage storage $ = _getAccessControlStorage();
                  if (hasRole(role, account)) {
                      $._roles[role].hasRole[account] = false;
                      emit RoleRevoked(role, account, _msgSender());
                      return true;
                  } else {
                      return false;
                  }
              }
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Metadata.sol)
          pragma solidity ^0.8.20;
          import {IERC20} from "../IERC20.sol";
          /**
           * @dev Interface for the optional metadata functions from the ERC-20 standard.
           */
          interface IERC20Metadata is IERC20 {
              /**
               * @dev Returns the name of the token.
               */
              function name() external view returns (string memory);
              /**
               * @dev Returns the symbol of the token.
               */
              function symbol() external view returns (string memory);
              /**
               * @dev Returns the decimals places of the token.
               */
              function decimals() external view returns (uint8);
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
          pragma solidity ^0.8.20;
          /**
           * @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 (last updated v5.0.0) (interfaces/draft-IERC6093.sol)
          pragma solidity ^0.8.20;
          /**
           * @dev Standard ERC-20 Errors
           * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-20 tokens.
           */
          interface IERC20Errors {
              /**
               * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
               * @param sender Address whose tokens are being transferred.
               * @param balance Current balance for the interacting account.
               * @param needed Minimum amount required to perform a transfer.
               */
              error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);
              /**
               * @dev Indicates a failure with the token `sender`. Used in transfers.
               * @param sender Address whose tokens are being transferred.
               */
              error ERC20InvalidSender(address sender);
              /**
               * @dev Indicates a failure with the token `receiver`. Used in transfers.
               * @param receiver Address to which tokens are being transferred.
               */
              error ERC20InvalidReceiver(address receiver);
              /**
               * @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.
               * @param spender Address that may be allowed to operate on tokens without being their owner.
               * @param allowance Amount of tokens a `spender` is allowed to operate with.
               * @param needed Minimum amount required to perform a transfer.
               */
              error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);
              /**
               * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
               * @param approver Address initiating an approval operation.
               */
              error ERC20InvalidApprover(address approver);
              /**
               * @dev Indicates a failure with the `spender` to be approved. Used in approvals.
               * @param spender Address that may be allowed to operate on tokens without being their owner.
               */
              error ERC20InvalidSpender(address spender);
          }
          /**
           * @dev Standard ERC-721 Errors
           * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-721 tokens.
           */
          interface IERC721Errors {
              /**
               * @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in ERC-20.
               * Used in balance queries.
               * @param owner Address of the current owner of a token.
               */
              error ERC721InvalidOwner(address owner);
              /**
               * @dev Indicates a `tokenId` whose `owner` is the zero address.
               * @param tokenId Identifier number of a token.
               */
              error ERC721NonexistentToken(uint256 tokenId);
              /**
               * @dev Indicates an error related to the ownership over a particular token. Used in transfers.
               * @param sender Address whose tokens are being transferred.
               * @param tokenId Identifier number of a token.
               * @param owner Address of the current owner of a token.
               */
              error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);
              /**
               * @dev Indicates a failure with the token `sender`. Used in transfers.
               * @param sender Address whose tokens are being transferred.
               */
              error ERC721InvalidSender(address sender);
              /**
               * @dev Indicates a failure with the token `receiver`. Used in transfers.
               * @param receiver Address to which tokens are being transferred.
               */
              error ERC721InvalidReceiver(address receiver);
              /**
               * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
               * @param operator Address that may be allowed to operate on tokens without being their owner.
               * @param tokenId Identifier number of a token.
               */
              error ERC721InsufficientApproval(address operator, uint256 tokenId);
              /**
               * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
               * @param approver Address initiating an approval operation.
               */
              error ERC721InvalidApprover(address approver);
              /**
               * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
               * @param operator Address that may be allowed to operate on tokens without being their owner.
               */
              error ERC721InvalidOperator(address operator);
          }
          /**
           * @dev Standard ERC-1155 Errors
           * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-1155 tokens.
           */
          interface IERC1155Errors {
              /**
               * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
               * @param sender Address whose tokens are being transferred.
               * @param balance Current balance for the interacting account.
               * @param needed Minimum amount required to perform a transfer.
               * @param tokenId Identifier number of a token.
               */
              error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);
              /**
               * @dev Indicates a failure with the token `sender`. Used in transfers.
               * @param sender Address whose tokens are being transferred.
               */
              error ERC1155InvalidSender(address sender);
              /**
               * @dev Indicates a failure with the token `receiver`. Used in transfers.
               * @param receiver Address to which tokens are being transferred.
               */
              error ERC1155InvalidReceiver(address receiver);
              /**
               * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
               * @param operator Address that may be allowed to operate on tokens without being their owner.
               * @param owner Address of the current owner of a token.
               */
              error ERC1155MissingApprovalForAll(address operator, address owner);
              /**
               * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
               * @param approver Address initiating an approval operation.
               */
              error ERC1155InvalidApprover(address approver);
              /**
               * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
               * @param operator Address that may be allowed to operate on tokens without being their owner.
               */
              error ERC1155InvalidOperator(address operator);
              /**
               * @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.
               * Used in batch transfers.
               * @param idsLength Length of the array of token identifiers
               * @param valuesLength Length of the array of token amounts
               */
              error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC1363.sol)
          pragma solidity ^0.8.20;
          import {IERC20} from "./IERC20.sol";
          import {IERC165} from "./IERC165.sol";
          /**
           * @title IERC1363
           * @dev Interface of the ERC-1363 standard as defined in the https://eips.ethereum.org/EIPS/eip-1363[ERC-1363].
           *
           * Defines an extension interface for ERC-20 tokens that supports executing code on a recipient contract
           * after `transfer` or `transferFrom`, or code on a spender contract after `approve`, in a single transaction.
           */
          interface IERC1363 is IERC20, IERC165 {
              /*
               * Note: the ERC-165 identifier for this interface is 0xb0202a11.
               * 0xb0202a11 ===
               *   bytes4(keccak256('transferAndCall(address,uint256)')) ^
               *   bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^
               *   bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^
               *   bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) ^
               *   bytes4(keccak256('approveAndCall(address,uint256)')) ^
               *   bytes4(keccak256('approveAndCall(address,uint256,bytes)'))
               */
              /**
               * @dev Moves a `value` amount of tokens from the caller's account to `to`
               * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
               * @param to The address which you want to transfer to.
               * @param value The amount of tokens to be transferred.
               * @return A boolean value indicating whether the operation succeeded unless throwing.
               */
              function transferAndCall(address to, uint256 value) external returns (bool);
              /**
               * @dev Moves a `value` amount of tokens from the caller's account to `to`
               * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
               * @param to The address which you want to transfer to.
               * @param value The amount of tokens to be transferred.
               * @param data Additional data with no specified format, sent in call to `to`.
               * @return A boolean value indicating whether the operation succeeded unless throwing.
               */
              function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool);
              /**
               * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
               * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
               * @param from The address which you want to send tokens from.
               * @param to The address which you want to transfer to.
               * @param value The amount of tokens to be transferred.
               * @return A boolean value indicating whether the operation succeeded unless throwing.
               */
              function transferFromAndCall(address from, address to, uint256 value) external returns (bool);
              /**
               * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
               * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
               * @param from The address which you want to send tokens from.
               * @param to The address which you want to transfer to.
               * @param value The amount of tokens to be transferred.
               * @param data Additional data with no specified format, sent in call to `to`.
               * @return A boolean value indicating whether the operation succeeded unless throwing.
               */
              function transferFromAndCall(address from, address to, uint256 value, bytes calldata data) external returns (bool);
              /**
               * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
               * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
               * @param spender The address which will spend the funds.
               * @param value The amount of tokens to be spent.
               * @return A boolean value indicating whether the operation succeeded unless throwing.
               */
              function approveAndCall(address spender, uint256 value) external returns (bool);
              /**
               * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
               * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
               * @param spender The address which will spend the funds.
               * @param value The amount of tokens to be spent.
               * @param data Additional data with no specified format, sent in call to `spender`.
               * @return A boolean value indicating whether the operation succeeded unless throwing.
               */
              function approveAndCall(address spender, uint256 value, bytes calldata data) external returns (bool);
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)
          pragma solidity ^0.8.20;
          import {Errors} from "./Errors.sol";
          /**
           * @dev Collection of functions related to the address type
           */
          library Address {
              /**
               * @dev There's no code at `target` (it is not a contract).
               */
              error AddressEmptyCode(address target);
              /**
               * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
               * `recipient`, forwarding all available gas and reverting on errors.
               *
               * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
               * of certain opcodes, possibly making contracts go over the 2300 gas limit
               * imposed by `transfer`, making them unable to receive funds via
               * `transfer`. {sendValue} removes this limitation.
               *
               * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
               *
               * IMPORTANT: because control is transferred to `recipient`, care must be
               * taken to not create reentrancy vulnerabilities. Consider using
               * {ReentrancyGuard} or the
               * https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
               */
              function sendValue(address payable recipient, uint256 amount) internal {
                  if (address(this).balance < amount) {
                      revert Errors.InsufficientBalance(address(this).balance, amount);
                  }
                  (bool success, ) = recipient.call{value: amount}("");
                  if (!success) {
                      revert Errors.FailedCall();
                  }
              }
              /**
               * @dev Performs a Solidity function call using a low level `call`. A
               * plain `call` is an unsafe replacement for a function call: use this
               * function instead.
               *
               * If `target` reverts with a revert reason or custom error, it is bubbled
               * up by this function (like regular Solidity function calls). However, if
               * the call reverted with no returned reason, this function reverts with a
               * {Errors.FailedCall} error.
               *
               * Returns the raw returned data. To convert to the expected return value,
               * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
               *
               * Requirements:
               *
               * - `target` must be a contract.
               * - calling `target` with `data` must not revert.
               */
              function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                  return functionCallWithValue(target, data, 0);
              }
              /**
               * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
               * but also transferring `value` wei to `target`.
               *
               * Requirements:
               *
               * - the calling contract must have an ETH balance of at least `value`.
               * - the called Solidity function must be `payable`.
               */
              function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
                  if (address(this).balance < value) {
                      revert Errors.InsufficientBalance(address(this).balance, value);
                  }
                  (bool success, bytes memory returndata) = target.call{value: value}(data);
                  return verifyCallResultFromTarget(target, success, returndata);
              }
              /**
               * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
               * but performing a static call.
               */
              function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                  (bool success, bytes memory returndata) = target.staticcall(data);
                  return verifyCallResultFromTarget(target, success, returndata);
              }
              /**
               * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
               * but performing a delegate call.
               */
              function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                  (bool success, bytes memory returndata) = target.delegatecall(data);
                  return verifyCallResultFromTarget(target, success, returndata);
              }
              /**
               * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
               * was not a contract or bubbling up the revert reason (falling back to {Errors.FailedCall}) in case
               * of an unsuccessful call.
               */
              function verifyCallResultFromTarget(
                  address target,
                  bool success,
                  bytes memory returndata
              ) internal view returns (bytes memory) {
                  if (!success) {
                      _revert(returndata);
                  } else {
                      // only check if target is a contract if the call was successful and the return data is empty
                      // otherwise we already know that it was a contract
                      if (returndata.length == 0 && target.code.length == 0) {
                          revert AddressEmptyCode(target);
                      }
                      return returndata;
                  }
              }
              /**
               * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
               * revert reason or with a default {Errors.FailedCall} error.
               */
              function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
                  if (!success) {
                      _revert(returndata);
                  } else {
                      return returndata;
                  }
              }
              /**
               * @dev Reverts with returndata if present. Otherwise reverts with {Errors.FailedCall}.
               */
              function _revert(bytes memory returndata) private pure {
                  // Look for revert reason and bubble it up if present
                  if (returndata.length > 0) {
                      // The easiest way to bubble the revert reason is using memory via assembly
                      assembly ("memory-safe") {
                          let returndata_size := mload(returndata)
                          revert(add(32, returndata), returndata_size)
                      }
                  } else {
                      revert Errors.FailedCall();
                  }
              }
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC1822.sol)
          pragma solidity ^0.8.20;
          /**
           * @dev ERC-1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified
           * proxy whose upgrades are fully controlled by the current implementation.
           */
          interface IERC1822Proxiable {
              /**
               * @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation
               * address.
               *
               * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
               * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
               * function revert if invoked through a proxy.
               */
              function proxiableUUID() external view returns (bytes32);
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v5.0.0) (proxy/ERC1967/ERC1967Utils.sol)
          pragma solidity ^0.8.21;
          import {IBeacon} from "../beacon/IBeacon.sol";
          import {IERC1967} from "../../interfaces/IERC1967.sol";
          import {Address} from "../../utils/Address.sol";
          import {StorageSlot} from "../../utils/StorageSlot.sol";
          /**
           * @dev This library provides getters and event emitting update functions for
           * https://eips.ethereum.org/EIPS/eip-1967[ERC-1967] slots.
           */
          library ERC1967Utils {
              /**
               * @dev Storage slot with the address of the current implementation.
               * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1.
               */
              // solhint-disable-next-line private-vars-leading-underscore
              bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
              /**
               * @dev The `implementation` of the proxy is invalid.
               */
              error ERC1967InvalidImplementation(address implementation);
              /**
               * @dev The `admin` of the proxy is invalid.
               */
              error ERC1967InvalidAdmin(address admin);
              /**
               * @dev The `beacon` of the proxy is invalid.
               */
              error ERC1967InvalidBeacon(address beacon);
              /**
               * @dev An upgrade function sees `msg.value > 0` that may be lost.
               */
              error ERC1967NonPayable();
              /**
               * @dev Returns the current implementation address.
               */
              function getImplementation() internal view returns (address) {
                  return StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value;
              }
              /**
               * @dev Stores a new address in the ERC-1967 implementation slot.
               */
              function _setImplementation(address newImplementation) private {
                  if (newImplementation.code.length == 0) {
                      revert ERC1967InvalidImplementation(newImplementation);
                  }
                  StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value = newImplementation;
              }
              /**
               * @dev Performs implementation upgrade with additional setup call if data is nonempty.
               * This function is payable only if the setup call is performed, otherwise `msg.value` is rejected
               * to avoid stuck value in the contract.
               *
               * Emits an {IERC1967-Upgraded} event.
               */
              function upgradeToAndCall(address newImplementation, bytes memory data) internal {
                  _setImplementation(newImplementation);
                  emit IERC1967.Upgraded(newImplementation);
                  if (data.length > 0) {
                      Address.functionDelegateCall(newImplementation, data);
                  } else {
                      _checkNonPayable();
                  }
              }
              /**
               * @dev Storage slot with the admin of the contract.
               * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1.
               */
              // solhint-disable-next-line private-vars-leading-underscore
              bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
              /**
               * @dev Returns the current admin.
               *
               * TIP: To get this value clients can read directly from the storage slot shown below (specified by ERC-1967) using
               * the https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
               * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
               */
              function getAdmin() internal view returns (address) {
                  return StorageSlot.getAddressSlot(ADMIN_SLOT).value;
              }
              /**
               * @dev Stores a new address in the ERC-1967 admin slot.
               */
              function _setAdmin(address newAdmin) private {
                  if (newAdmin == address(0)) {
                      revert ERC1967InvalidAdmin(address(0));
                  }
                  StorageSlot.getAddressSlot(ADMIN_SLOT).value = newAdmin;
              }
              /**
               * @dev Changes the admin of the proxy.
               *
               * Emits an {IERC1967-AdminChanged} event.
               */
              function changeAdmin(address newAdmin) internal {
                  emit IERC1967.AdminChanged(getAdmin(), newAdmin);
                  _setAdmin(newAdmin);
              }
              /**
               * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
               * This is the keccak-256 hash of "eip1967.proxy.beacon" subtracted by 1.
               */
              // solhint-disable-next-line private-vars-leading-underscore
              bytes32 internal constant BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
              /**
               * @dev Returns the current beacon.
               */
              function getBeacon() internal view returns (address) {
                  return StorageSlot.getAddressSlot(BEACON_SLOT).value;
              }
              /**
               * @dev Stores a new beacon in the ERC-1967 beacon slot.
               */
              function _setBeacon(address newBeacon) private {
                  if (newBeacon.code.length == 0) {
                      revert ERC1967InvalidBeacon(newBeacon);
                  }
                  StorageSlot.getAddressSlot(BEACON_SLOT).value = newBeacon;
                  address beaconImplementation = IBeacon(newBeacon).implementation();
                  if (beaconImplementation.code.length == 0) {
                      revert ERC1967InvalidImplementation(beaconImplementation);
                  }
              }
              /**
               * @dev Change the beacon and trigger a setup call if data is nonempty.
               * This function is payable only if the setup call is performed, otherwise `msg.value` is rejected
               * to avoid stuck value in the contract.
               *
               * Emits an {IERC1967-BeaconUpgraded} event.
               *
               * CAUTION: Invoking this function has no effect on an instance of {BeaconProxy} since v5, since
               * it uses an immutable beacon without looking at the value of the ERC-1967 beacon slot for
               * efficiency.
               */
              function upgradeBeaconToAndCall(address newBeacon, bytes memory data) internal {
                  _setBeacon(newBeacon);
                  emit IERC1967.BeaconUpgraded(newBeacon);
                  if (data.length > 0) {
                      Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
                  } else {
                      _checkNonPayable();
                  }
              }
              /**
               * @dev Reverts if `msg.value` is not zero. It can be used to avoid `msg.value` stuck in the contract
               * if an upgrade doesn't perform an initialization call.
               */
              function _checkNonPayable() private {
                  if (msg.value > 0) {
                      revert ERC1967NonPayable();
                  }
              }
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol)
          pragma solidity ^0.8.20;
          /**
           * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
           * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
           * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
           * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
           *
           * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
           * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
           * case an upgrade adds a module that needs to be initialized.
           *
           * For example:
           *
           * [.hljs-theme-light.nopadding]
           * ```solidity
           * contract MyToken is ERC20Upgradeable {
           *     function initialize() initializer public {
           *         __ERC20_init("MyToken", "MTK");
           *     }
           * }
           *
           * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
           *     function initializeV2() reinitializer(2) public {
           *         __ERC20Permit_init("MyToken");
           *     }
           * }
           * ```
           *
           * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
           * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
           *
           * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
           * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
           *
           * [CAUTION]
           * ====
           * Avoid leaving a contract uninitialized.
           *
           * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
           * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
           * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
           *
           * [.hljs-theme-light.nopadding]
           * ```
           * /// @custom:oz-upgrades-unsafe-allow constructor
           * constructor() {
           *     _disableInitializers();
           * }
           * ```
           * ====
           */
          abstract contract Initializable {
              /**
               * @dev Storage of the initializable contract.
               *
               * It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
               * when using with upgradeable contracts.
               *
               * @custom:storage-location erc7201:openzeppelin.storage.Initializable
               */
              struct InitializableStorage {
                  /**
                   * @dev Indicates that the contract has been initialized.
                   */
                  uint64 _initialized;
                  /**
                   * @dev Indicates that the contract is in the process of being initialized.
                   */
                  bool _initializing;
              }
              // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
              bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;
              /**
               * @dev The contract is already initialized.
               */
              error InvalidInitialization();
              /**
               * @dev The contract is not initializing.
               */
              error NotInitializing();
              /**
               * @dev Triggered when the contract has been initialized or reinitialized.
               */
              event Initialized(uint64 version);
              /**
               * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
               * `onlyInitializing` functions can be used to initialize parent contracts.
               *
               * Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
               * number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
               * production.
               *
               * Emits an {Initialized} event.
               */
              modifier initializer() {
                  // solhint-disable-next-line var-name-mixedcase
                  InitializableStorage storage $ = _getInitializableStorage();
                  // Cache values to avoid duplicated sloads
                  bool isTopLevelCall = !$._initializing;
                  uint64 initialized = $._initialized;
                  // Allowed calls:
                  // - initialSetup: the contract is not in the initializing state and no previous version was
                  //                 initialized
                  // - construction: the contract is initialized at version 1 (no reininitialization) and the
                  //                 current contract is just being deployed
                  bool initialSetup = initialized == 0 && isTopLevelCall;
                  bool construction = initialized == 1 && address(this).code.length == 0;
                  if (!initialSetup && !construction) {
                      revert InvalidInitialization();
                  }
                  $._initialized = 1;
                  if (isTopLevelCall) {
                      $._initializing = true;
                  }
                  _;
                  if (isTopLevelCall) {
                      $._initializing = false;
                      emit Initialized(1);
                  }
              }
              /**
               * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
               * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
               * used to initialize parent contracts.
               *
               * A reinitializer may be used after the original initialization step. This is essential to configure modules that
               * are added through upgrades and that require initialization.
               *
               * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
               * cannot be nested. If one is invoked in the context of another, execution will revert.
               *
               * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
               * a contract, executing them in the right order is up to the developer or operator.
               *
               * WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.
               *
               * Emits an {Initialized} event.
               */
              modifier reinitializer(uint64 version) {
                  // solhint-disable-next-line var-name-mixedcase
                  InitializableStorage storage $ = _getInitializableStorage();
                  if ($._initializing || $._initialized >= version) {
                      revert InvalidInitialization();
                  }
                  $._initialized = version;
                  $._initializing = true;
                  _;
                  $._initializing = false;
                  emit Initialized(version);
              }
              /**
               * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
               * {initializer} and {reinitializer} modifiers, directly or indirectly.
               */
              modifier onlyInitializing() {
                  _checkInitializing();
                  _;
              }
              /**
               * @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
               */
              function _checkInitializing() internal view virtual {
                  if (!_isInitializing()) {
                      revert NotInitializing();
                  }
              }
              /**
               * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
               * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
               * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
               * through proxies.
               *
               * Emits an {Initialized} event the first time it is successfully executed.
               */
              function _disableInitializers() internal virtual {
                  // solhint-disable-next-line var-name-mixedcase
                  InitializableStorage storage $ = _getInitializableStorage();
                  if ($._initializing) {
                      revert InvalidInitialization();
                  }
                  if ($._initialized != type(uint64).max) {
                      $._initialized = type(uint64).max;
                      emit Initialized(type(uint64).max);
                  }
              }
              /**
               * @dev Returns the highest version that has been initialized. See {reinitializer}.
               */
              function _getInitializedVersion() internal view returns (uint64) {
                  return _getInitializableStorage()._initialized;
              }
              /**
               * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
               */
              function _isInitializing() internal view returns (bool) {
                  return _getInitializableStorage()._initializing;
              }
              /**
               * @dev Returns a pointer to the storage namespace.
               */
              // solhint-disable-next-line var-name-mixedcase
              function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
                  assembly {
                      $.slot := INITIALIZABLE_STORAGE
                  }
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.20;
          /**
           * @dev Library of standard hash functions.
           *
           * _Available since v5.1._
           */
          library Hashes {
              /**
               * @dev Commutative Keccak256 hash of a sorted pair of bytes32. Frequently used when working with merkle proofs.
               *
               * NOTE: Equivalent to the `standardNodeHash` in our https://github.com/OpenZeppelin/merkle-tree[JavaScript library].
               */
              function commutativeKeccak256(bytes32 a, bytes32 b) internal pure returns (bytes32) {
                  return a < b ? _efficientKeccak256(a, b) : _efficientKeccak256(b, a);
              }
              /**
               * @dev Implementation of keccak256(abi.encode(a, b)) that doesn't allocate or expand memory.
               */
              function _efficientKeccak256(bytes32 a, bytes32 b) private pure returns (bytes32 value) {
                  assembly ("memory-safe") {
                      mstore(0x00, a)
                      mstore(0x20, b)
                      value := keccak256(0x00, 0x40)
                  }
              }
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v5.0.0) (access/IAccessControl.sol)
          pragma solidity ^0.8.20;
          /**
           * @dev External interface of AccessControl declared to support ERC-165 detection.
           */
          interface IAccessControl {
              /**
               * @dev The `account` is missing a role.
               */
              error AccessControlUnauthorizedAccount(address account, bytes32 neededRole);
              /**
               * @dev The caller of a function is not the expected one.
               *
               * NOTE: Don't confuse with {AccessControlUnauthorizedAccount}.
               */
              error AccessControlBadConfirmation();
              /**
               * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
               *
               * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
               * {RoleAdminChanged} not being emitted signaling this.
               */
              event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
              /**
               * @dev Emitted when `account` is granted `role`.
               *
               * `sender` is the account that originated the contract call. This account bears the admin role (for the granted role).
               * Expected in cases where the role was granted using the internal {AccessControl-_grantRole}.
               */
              event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
              /**
               * @dev Emitted when `account` is revoked `role`.
               *
               * `sender` is the account that originated the contract call:
               *   - if using `revokeRole`, it is the admin role bearer
               *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
               */
              event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
              /**
               * @dev Returns `true` if `account` has been granted `role`.
               */
              function hasRole(bytes32 role, address account) external view returns (bool);
              /**
               * @dev Returns the admin role that controls `role`. See {grantRole} and
               * {revokeRole}.
               *
               * To change a role's admin, use {AccessControl-_setRoleAdmin}.
               */
              function getRoleAdmin(bytes32 role) external view returns (bytes32);
              /**
               * @dev Grants `role` to `account`.
               *
               * If `account` had not been already granted `role`, emits a {RoleGranted}
               * event.
               *
               * Requirements:
               *
               * - the caller must have ``role``'s admin role.
               */
              function grantRole(bytes32 role, address account) external;
              /**
               * @dev Revokes `role` from `account`.
               *
               * If `account` had been granted `role`, emits a {RoleRevoked} event.
               *
               * Requirements:
               *
               * - the caller must have ``role``'s admin role.
               */
              function revokeRole(bytes32 role, address account) external;
              /**
               * @dev Revokes `role` from the calling account.
               *
               * Roles are often managed via {grantRole} and {revokeRole}: this function's
               * purpose is to provide a mechanism for accounts to lose their privileges
               * if they are compromised (such as when a trusted device is misplaced).
               *
               * If the calling account had been granted `role`, emits a {RoleRevoked}
               * event.
               *
               * Requirements:
               *
               * - the caller must be `callerConfirmation`.
               */
              function renounceRole(bytes32 role, address callerConfirmation) external;
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
          pragma solidity ^0.8.20;
          import {Initializable} from "../proxy/utils/Initializable.sol";
          /**
           * @dev Provides information about the current execution context, including the
           * sender of the transaction and its data. While these are generally available
           * via msg.sender and msg.data, they should not be accessed in such a direct
           * manner, since when dealing with meta-transactions the account sending and
           * paying for execution may not be the actual sender (as far as an application
           * is concerned).
           *
           * This contract is only required for intermediate, library-like contracts.
           */
          abstract contract ContextUpgradeable is Initializable {
              function __Context_init() internal onlyInitializing {
              }
              function __Context_init_unchained() internal onlyInitializing {
              }
              function _msgSender() internal view virtual returns (address) {
                  return msg.sender;
              }
              function _msgData() internal view virtual returns (bytes calldata) {
                  return msg.data;
              }
              function _contextSuffixLength() internal view virtual returns (uint256) {
                  return 0;
              }
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/ERC165.sol)
          pragma solidity ^0.8.20;
          import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
          import {Initializable} from "../../proxy/utils/Initializable.sol";
          /**
           * @dev Implementation of the {IERC165} interface.
           *
           * Contracts that want to implement ERC-165 should inherit from this contract and override {supportsInterface} to check
           * for the additional interface id that will be supported. For example:
           *
           * ```solidity
           * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
           *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
           * }
           * ```
           */
          abstract contract ERC165Upgradeable is Initializable, IERC165 {
              function __ERC165_init() internal onlyInitializing {
              }
              function __ERC165_init_unchained() internal onlyInitializing {
              }
              /**
               * @dev See {IERC165-supportsInterface}.
               */
              function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
                  return interfaceId == type(IERC165).interfaceId;
              }
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC20.sol)
          pragma solidity ^0.8.20;
          import {IERC20} from "../token/ERC20/IERC20.sol";
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC165.sol)
          pragma solidity ^0.8.20;
          import {IERC165} from "../utils/introspection/IERC165.sol";
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.20;
          /**
           * @dev Collection of common custom errors used in multiple contracts
           *
           * IMPORTANT: Backwards compatibility is not guaranteed in future versions of the library.
           * It is recommended to avoid relying on the error API for critical functionality.
           *
           * _Available since v5.1._
           */
          library Errors {
              /**
               * @dev The ETH balance of the account is not enough to perform the operation.
               */
              error InsufficientBalance(uint256 balance, uint256 needed);
              /**
               * @dev A call to an address target failed. The target may have reverted.
               */
              error FailedCall();
              /**
               * @dev The deployment failed.
               */
              error FailedDeployment();
              /**
               * @dev A necessary precompile is missing.
               */
              error MissingPrecompile(address);
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v5.0.0) (proxy/beacon/IBeacon.sol)
          pragma solidity ^0.8.20;
          /**
           * @dev This is the interface that {BeaconProxy} expects of its beacon.
           */
          interface IBeacon {
              /**
               * @dev Must return an address that can be used as a delegate call target.
               *
               * {UpgradeableBeacon} will check that this address is a contract.
               */
              function implementation() external view returns (address);
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC1967.sol)
          pragma solidity ^0.8.20;
          /**
           * @dev ERC-1967: Proxy Storage Slots. This interface contains the events defined in the ERC.
           */
          interface IERC1967 {
              /**
               * @dev Emitted when the implementation is upgraded.
               */
              event Upgraded(address indexed implementation);
              /**
               * @dev Emitted when the admin account has changed.
               */
              event AdminChanged(address previousAdmin, address newAdmin);
              /**
               * @dev Emitted when the beacon is changed.
               */
              event BeaconUpgraded(address indexed beacon);
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v5.0.0) (utils/StorageSlot.sol)
          // This file was procedurally generated from scripts/generate/templates/StorageSlot.js.
          pragma solidity ^0.8.20;
          /**
           * @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 ERC-1967 implementation slot:
           * ```solidity
           * contract ERC1967 {
           *     // Define the slot. Alternatively, use the SlotDerivation library to derive the slot.
           *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
           *
           *     function _getImplementation() internal view returns (address) {
           *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
           *     }
           *
           *     function _setImplementation(address newImplementation) internal {
           *         require(newImplementation.code.length > 0);
           *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
           *     }
           * }
           * ```
           *
           * TIP: Consider using this library along with {SlotDerivation}.
           */
          library StorageSlot {
              struct AddressSlot {
                  address value;
              }
              struct BooleanSlot {
                  bool value;
              }
              struct Bytes32Slot {
                  bytes32 value;
              }
              struct Uint256Slot {
                  uint256 value;
              }
              struct Int256Slot {
                  int256 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) {
                  assembly ("memory-safe") {
                      r.slot := slot
                  }
              }
              /**
               * @dev Returns a `BooleanSlot` with member `value` located at `slot`.
               */
              function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
                  assembly ("memory-safe") {
                      r.slot := slot
                  }
              }
              /**
               * @dev Returns a `Bytes32Slot` with member `value` located at `slot`.
               */
              function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
                  assembly ("memory-safe") {
                      r.slot := slot
                  }
              }
              /**
               * @dev Returns a `Uint256Slot` with member `value` located at `slot`.
               */
              function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
                  assembly ("memory-safe") {
                      r.slot := slot
                  }
              }
              /**
               * @dev Returns a `Int256Slot` with member `value` located at `slot`.
               */
              function getInt256Slot(bytes32 slot) internal pure returns (Int256Slot storage r) {
                  assembly ("memory-safe") {
                      r.slot := slot
                  }
              }
              /**
               * @dev Returns a `StringSlot` with member `value` located at `slot`.
               */
              function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
                  assembly ("memory-safe") {
                      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) {
                  assembly ("memory-safe") {
                      r.slot := store.slot
                  }
              }
              /**
               * @dev Returns a `BytesSlot` with member `value` located at `slot`.
               */
              function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
                  assembly ("memory-safe") {
                      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) {
                  assembly ("memory-safe") {
                      r.slot := store.slot
                  }
              }
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)
          pragma solidity ^0.8.20;
          /**
           * @dev Interface of the ERC-165 standard, as defined in the
           * https://eips.ethereum.org/EIPS/eip-165[ERC].
           *
           * Implementers can declare support of contract interfaces, which can then be
           * queried by others ({ERC165Checker}).
           *
           * For an implementation, see {ERC165}.
           */
          interface IERC165 {
              /**
               * @dev Returns true if this contract implements the interface defined by
               * `interfaceId`. See the corresponding
               * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]
               * to learn more about how these ids are created.
               *
               * This function call must use less than 30 000 gas.
               */
              function supportsInterface(bytes4 interfaceId) external view returns (bool);
          }
          

          File 3 of 4: EACAggregatorProxy
          pragma solidity 0.6.6;
          
          
          /**
           * @title The Owned contract
           * @notice A contract with helpers for basic contract ownership.
           */
          contract Owned {
          
            address public owner;
            address private pendingOwner;
          
            event OwnershipTransferRequested(
              address indexed from,
              address indexed to
            );
            event OwnershipTransferred(
              address indexed from,
              address indexed to
            );
          
            constructor() public {
              owner = msg.sender;
            }
          
            /**
             * @dev Allows an owner to begin transferring ownership to a new address,
             * pending.
             */
            function transferOwnership(address _to)
              external
              onlyOwner()
            {
              pendingOwner = _to;
          
              emit OwnershipTransferRequested(owner, _to);
            }
          
            /**
             * @dev Allows an ownership transfer to be completed by the recipient.
             */
            function acceptOwnership()
              external
            {
              require(msg.sender == pendingOwner, "Must be proposed owner");
          
              address oldOwner = owner;
              owner = msg.sender;
              pendingOwner = address(0);
          
              emit OwnershipTransferred(oldOwner, msg.sender);
            }
          
            /**
             * @dev Reverts if called by anyone other than the contract owner.
             */
            modifier onlyOwner() {
              require(msg.sender == owner, "Only callable by owner");
              _;
            }
          
          }
          
          interface AggregatorInterface {
            function latestAnswer() external view returns (int256);
            function latestTimestamp() external view returns (uint256);
            function latestRound() external view returns (uint256);
            function getAnswer(uint256 roundId) external view returns (int256);
            function getTimestamp(uint256 roundId) external view returns (uint256);
          
            event AnswerUpdated(int256 indexed current, uint256 indexed roundId, uint256 updatedAt);
            event NewRound(uint256 indexed roundId, address indexed startedBy, uint256 startedAt);
          }
          
          interface AggregatorV3Interface {
          
            function decimals() external view returns (uint8);
            function description() external view returns (string memory);
            function version() external view returns (uint256);
          
            // getRoundData and latestRoundData should both raise "No data present"
            // if they do not have data to report, instead of returning unset values
            // which could be misinterpreted as actual reported values.
            function getRoundData(uint80 _roundId)
              external
              view
              returns (
                uint80 roundId,
                int256 answer,
                uint256 startedAt,
                uint256 updatedAt,
                uint80 answeredInRound
              );
            function latestRoundData()
              external
              view
              returns (
                uint80 roundId,
                int256 answer,
                uint256 startedAt,
                uint256 updatedAt,
                uint80 answeredInRound
              );
          
          }
          
          interface AggregatorV2V3Interface is AggregatorInterface, AggregatorV3Interface
          {
          }
          
          /**
           * @title A trusted proxy for updating where current answers are read from
           * @notice This contract provides a consistent address for the
           * CurrentAnwerInterface but delegates where it reads from to the owner, who is
           * trusted to update it.
           */
          contract AggregatorProxy is AggregatorV2V3Interface, Owned {
          
            struct Phase {
              uint16 id;
              AggregatorV2V3Interface aggregator;
            }
            Phase private currentPhase;
            AggregatorV2V3Interface public proposedAggregator;
            mapping(uint16 => AggregatorV2V3Interface) public phaseAggregators;
          
            uint256 constant private PHASE_OFFSET = 64;
            uint256 constant private PHASE_SIZE = 16;
            uint256 constant private MAX_ID = 2**(PHASE_OFFSET+PHASE_SIZE) - 1;
          
            constructor(address _aggregator) public Owned() {
              setAggregator(_aggregator);
            }
          
            /**
             * @notice Reads the current answer from aggregator delegated to.
             *
             * @dev #[deprecated] Use latestRoundData instead. This does not error if no
             * answer has been reached, it will simply return 0. Either wait to point to
             * an already answered Aggregator or use the recommended latestRoundData
             * instead which includes better verification information.
             */
            function latestAnswer()
              public
              view
              virtual
              override
              returns (int256 answer)
            {
              return currentPhase.aggregator.latestAnswer();
            }
          
            /**
             * @notice Reads the last updated height from aggregator delegated to.
             *
             * @dev #[deprecated] Use latestRoundData instead. This does not error if no
             * answer has been reached, it will simply return 0. Either wait to point to
             * an already answered Aggregator or use the recommended latestRoundData
             * instead which includes better verification information.
             */
            function latestTimestamp()
              public
              view
              virtual
              override
              returns (uint256 updatedAt)
            {
              return currentPhase.aggregator.latestTimestamp();
            }
          
            /**
             * @notice get past rounds answers
             * @param _roundId the answer number to retrieve the answer for
             *
             * @dev #[deprecated] Use getRoundData instead. This does not error if no
             * answer has been reached, it will simply return 0. Either wait to point to
             * an already answered Aggregator or use the recommended getRoundData
             * instead which includes better verification information.
             */
            function getAnswer(uint256 _roundId)
              public
              view
              virtual
              override
              returns (int256 answer)
            {
              if (_roundId > MAX_ID) return 0;
          
              (uint16 phaseId, uint64 aggregatorRoundId) = parseIds(_roundId);
              AggregatorV2V3Interface aggregator = phaseAggregators[phaseId];
              if (address(aggregator) == address(0)) return 0;
          
              return aggregator.getAnswer(aggregatorRoundId);
            }
          
            /**
             * @notice get block timestamp when an answer was last updated
             * @param _roundId the answer number to retrieve the updated timestamp for
             *
             * @dev #[deprecated] Use getRoundData instead. This does not error if no
             * answer has been reached, it will simply return 0. Either wait to point to
             * an already answered Aggregator or use the recommended getRoundData
             * instead which includes better verification information.
             */
            function getTimestamp(uint256 _roundId)
              public
              view
              virtual
              override
              returns (uint256 updatedAt)
            {
              if (_roundId > MAX_ID) return 0;
          
              (uint16 phaseId, uint64 aggregatorRoundId) = parseIds(_roundId);
              AggregatorV2V3Interface aggregator = phaseAggregators[phaseId];
              if (address(aggregator) == address(0)) return 0;
          
              return aggregator.getTimestamp(aggregatorRoundId);
            }
          
            /**
             * @notice get the latest completed round where the answer was updated. This
             * ID includes the proxy's phase, to make sure round IDs increase even when
             * switching to a newly deployed aggregator.
             *
             * @dev #[deprecated] Use latestRoundData instead. This does not error if no
             * answer has been reached, it will simply return 0. Either wait to point to
             * an already answered Aggregator or use the recommended latestRoundData
             * instead which includes better verification information.
             */
            function latestRound()
              public
              view
              virtual
              override
              returns (uint256 roundId)
            {
              Phase memory phase = currentPhase; // cache storage reads
              return addPhase(phase.id, uint64(phase.aggregator.latestRound()));
            }
          
            /**
             * @notice get data about a round. Consumers are encouraged to check
             * that they're receiving fresh data by inspecting the updatedAt and
             * answeredInRound return values.
             * Note that different underlying implementations of AggregatorV3Interface
             * have slightly different semantics for some of the return values. Consumers
             * should determine what implementations they expect to receive
             * data from and validate that they can properly handle return data from all
             * of them.
             * @param _roundId the requested round ID as presented through the proxy, this
             * is made up of the aggregator's round ID with the phase ID encoded in the
             * two highest order bytes
             * @return roundId is the round ID from the aggregator for which the data was
             * retrieved combined with an phase to ensure that round IDs get larger as
             * time moves forward.
             * @return answer is the answer for the given round
             * @return startedAt is the timestamp when the round was started.
             * (Only some AggregatorV3Interface implementations return meaningful values)
             * @return updatedAt is the timestamp when the round last was updated (i.e.
             * answer was last computed)
             * @return answeredInRound is the round ID of the round in which the answer
             * was computed.
             * (Only some AggregatorV3Interface implementations return meaningful values)
             * @dev Note that answer and updatedAt may change between queries.
             */
            function getRoundData(uint80 _roundId)
              public
              view
              virtual
              override
              returns (
                uint80 roundId,
                int256 answer,
                uint256 startedAt,
                uint256 updatedAt,
                uint80 answeredInRound
              )
            {
              (uint16 phaseId, uint64 aggregatorRoundId) = parseIds(_roundId);
          
              (
                uint80 roundId,
                int256 answer,
                uint256 startedAt,
                uint256 updatedAt,
                uint80 ansIn
              ) = phaseAggregators[phaseId].getRoundData(aggregatorRoundId);
          
              return addPhaseIds(roundId, answer, startedAt, updatedAt, ansIn, phaseId);
            }
          
            /**
             * @notice get data about the latest round. Consumers are encouraged to check
             * that they're receiving fresh data by inspecting the updatedAt and
             * answeredInRound return values.
             * Note that different underlying implementations of AggregatorV3Interface
             * have slightly different semantics for some of the return values. Consumers
             * should determine what implementations they expect to receive
             * data from and validate that they can properly handle return data from all
             * of them.
             * @return roundId is the round ID from the aggregator for which the data was
             * retrieved combined with an phase to ensure that round IDs get larger as
             * time moves forward.
             * @return answer is the answer for the given round
             * @return startedAt is the timestamp when the round was started.
             * (Only some AggregatorV3Interface implementations return meaningful values)
             * @return updatedAt is the timestamp when the round last was updated (i.e.
             * answer was last computed)
             * @return answeredInRound is the round ID of the round in which the answer
             * was computed.
             * (Only some AggregatorV3Interface implementations return meaningful values)
             * @dev Note that answer and updatedAt may change between queries.
             */
            function latestRoundData()
              public
              view
              virtual
              override
              returns (
                uint80 roundId,
                int256 answer,
                uint256 startedAt,
                uint256 updatedAt,
                uint80 answeredInRound
              )
            {
              Phase memory current = currentPhase; // cache storage reads
          
              (
                uint80 roundId,
                int256 answer,
                uint256 startedAt,
                uint256 updatedAt,
                uint80 ansIn
              ) = current.aggregator.latestRoundData();
          
              return addPhaseIds(roundId, answer, startedAt, updatedAt, ansIn, current.id);
            }
          
            /**
             * @notice Used if an aggregator contract has been proposed.
             * @param _roundId the round ID to retrieve the round data for
             * @return roundId is the round ID for which data was retrieved
             * @return answer is the answer for the given round
             * @return startedAt is the timestamp when the round was started.
             * (Only some AggregatorV3Interface implementations return meaningful values)
             * @return updatedAt is the timestamp when the round last was updated (i.e.
             * answer was last computed)
             * @return answeredInRound is the round ID of the round in which the answer
             * was computed.
            */
            function proposedGetRoundData(uint80 _roundId)
              public
              view
              virtual
              hasProposal()
              returns (
                uint80 roundId,
                int256 answer,
                uint256 startedAt,
                uint256 updatedAt,
                uint80 answeredInRound
              )
            {
              return proposedAggregator.getRoundData(_roundId);
            }
          
            /**
             * @notice Used if an aggregator contract has been proposed.
             * @return roundId is the round ID for which data was retrieved
             * @return answer is the answer for the given round
             * @return startedAt is the timestamp when the round was started.
             * (Only some AggregatorV3Interface implementations return meaningful values)
             * @return updatedAt is the timestamp when the round last was updated (i.e.
             * answer was last computed)
             * @return answeredInRound is the round ID of the round in which the answer
             * was computed.
            */
            function proposedLatestRoundData()
              public
              view
              virtual
              hasProposal()
              returns (
                uint80 roundId,
                int256 answer,
                uint256 startedAt,
                uint256 updatedAt,
                uint80 answeredInRound
              )
            {
              return proposedAggregator.latestRoundData();
            }
          
            /**
             * @notice returns the current phase's aggregator address.
             */
            function aggregator()
              external
              view
              returns (address)
            {
              return address(currentPhase.aggregator);
            }
          
            /**
             * @notice returns the current phase's ID.
             */
            function phaseId()
              external
              view
              returns (uint16)
            {
              return currentPhase.id;
            }
          
            /**
             * @notice represents the number of decimals the aggregator responses represent.
             */
            function decimals()
              external
              view
              override
              returns (uint8)
            {
              return currentPhase.aggregator.decimals();
            }
          
            /**
             * @notice the version number representing the type of aggregator the proxy
             * points to.
             */
            function version()
              external
              view
              override
              returns (uint256)
            {
              return currentPhase.aggregator.version();
            }
          
            /**
             * @notice returns the description of the aggregator the proxy points to.
             */
            function description()
              external
              view
              override
              returns (string memory)
            {
              return currentPhase.aggregator.description();
            }
          
            /**
             * @notice Allows the owner to propose a new address for the aggregator
             * @param _aggregator The new address for the aggregator contract
             */
            function proposeAggregator(address _aggregator)
              external
              onlyOwner()
            {
              proposedAggregator = AggregatorV2V3Interface(_aggregator);
            }
          
            /**
             * @notice Allows the owner to confirm and change the address
             * to the proposed aggregator
             * @dev Reverts if the given address doesn't match what was previously
             * proposed
             * @param _aggregator The new address for the aggregator contract
             */
            function confirmAggregator(address _aggregator)
              external
              onlyOwner()
            {
              require(_aggregator == address(proposedAggregator), "Invalid proposed aggregator");
              delete proposedAggregator;
              setAggregator(_aggregator);
            }
          
          
            /*
             * Internal
             */
          
            function setAggregator(address _aggregator)
              internal
            {
              uint16 id = currentPhase.id + 1;
              currentPhase = Phase(id, AggregatorV2V3Interface(_aggregator));
              phaseAggregators[id] = AggregatorV2V3Interface(_aggregator);
            }
          
            function addPhase(
              uint16 _phase,
              uint64 _originalId
            )
              internal
              view
              returns (uint80)
            {
              return uint80(uint256(_phase) << PHASE_OFFSET | _originalId);
            }
          
            function parseIds(
              uint256 _roundId
            )
              internal
              view
              returns (uint16, uint64)
            {
              uint16 phaseId = uint16(_roundId >> PHASE_OFFSET);
              uint64 aggregatorRoundId = uint64(_roundId);
          
              return (phaseId, aggregatorRoundId);
            }
          
            function addPhaseIds(
                uint80 roundId,
                int256 answer,
                uint256 startedAt,
                uint256 updatedAt,
                uint80 answeredInRound,
                uint16 phaseId
            )
              internal
              view
              returns (uint80, int256, uint256, uint256, uint80)
            {
              return (
                addPhase(phaseId, uint64(roundId)),
                answer,
                startedAt,
                updatedAt,
                addPhase(phaseId, uint64(answeredInRound))
              );
            }
          
            /*
             * Modifiers
             */
          
            modifier hasProposal() {
              require(address(proposedAggregator) != address(0), "No proposed aggregator present");
              _;
            }
          
          }
          
          interface AccessControllerInterface {
            function hasAccess(address user, bytes calldata data) external view returns (bool);
          }
          
          /**
           * @title External Access Controlled Aggregator Proxy
           * @notice A trusted proxy for updating where current answers are read from
           * @notice This contract provides a consistent address for the
           * Aggregator and AggregatorV3Interface but delegates where it reads from to the owner, who is
           * trusted to update it.
           * @notice Only access enabled addresses are allowed to access getters for
           * aggregated answers and round information.
           */
          contract EACAggregatorProxy is AggregatorProxy {
          
            AccessControllerInterface public accessController;
          
            constructor(
              address _aggregator,
              address _accessController
            )
              public
              AggregatorProxy(_aggregator)
            {
              setController(_accessController);
            }
          
            /**
             * @notice Allows the owner to update the accessController contract address.
             * @param _accessController The new address for the accessController contract
             */
            function setController(address _accessController)
              public
              onlyOwner()
            {
              accessController = AccessControllerInterface(_accessController);
            }
          
            /**
             * @notice Reads the current answer from aggregator delegated to.
             * @dev overridden function to add the checkAccess() modifier
             *
             * @dev #[deprecated] Use latestRoundData instead. This does not error if no
             * answer has been reached, it will simply return 0. Either wait to point to
             * an already answered Aggregator or use the recommended latestRoundData
             * instead which includes better verification information.
             */
            function latestAnswer()
              public
              view
              override
              checkAccess()
              returns (int256)
            {
              return super.latestAnswer();
            }
          
            /**
             * @notice get the latest completed round where the answer was updated. This
             * ID includes the proxy's phase, to make sure round IDs increase even when
             * switching to a newly deployed aggregator.
             *
             * @dev #[deprecated] Use latestRoundData instead. This does not error if no
             * answer has been reached, it will simply return 0. Either wait to point to
             * an already answered Aggregator or use the recommended latestRoundData
             * instead which includes better verification information.
             */
            function latestTimestamp()
              public
              view
              override
              checkAccess()
              returns (uint256)
            {
              return super.latestTimestamp();
            }
          
            /**
             * @notice get past rounds answers
             * @param _roundId the answer number to retrieve the answer for
             * @dev overridden function to add the checkAccess() modifier
             *
             * @dev #[deprecated] Use getRoundData instead. This does not error if no
             * answer has been reached, it will simply return 0. Either wait to point to
             * an already answered Aggregator or use the recommended getRoundData
             * instead which includes better verification information.
             */
            function getAnswer(uint256 _roundId)
              public
              view
              override
              checkAccess()
              returns (int256)
            {
              return super.getAnswer(_roundId);
            }
          
            /**
             * @notice get block timestamp when an answer was last updated
             * @param _roundId the answer number to retrieve the updated timestamp for
             * @dev overridden function to add the checkAccess() modifier
             *
             * @dev #[deprecated] Use getRoundData instead. This does not error if no
             * answer has been reached, it will simply return 0. Either wait to point to
             * an already answered Aggregator or use the recommended getRoundData
             * instead which includes better verification information.
             */
            function getTimestamp(uint256 _roundId)
              public
              view
              override
              checkAccess()
              returns (uint256)
            {
              return super.getTimestamp(_roundId);
            }
          
            /**
             * @notice get the latest completed round where the answer was updated
             * @dev overridden function to add the checkAccess() modifier
             *
             * @dev #[deprecated] Use latestRoundData instead. This does not error if no
             * answer has been reached, it will simply return 0. Either wait to point to
             * an already answered Aggregator or use the recommended latestRoundData
             * instead which includes better verification information.
             */
            function latestRound()
              public
              view
              override
              checkAccess()
              returns (uint256)
            {
              return super.latestRound();
            }
          
            /**
             * @notice get data about a round. Consumers are encouraged to check
             * that they're receiving fresh data by inspecting the updatedAt and
             * answeredInRound return values.
             * Note that different underlying implementations of AggregatorV3Interface
             * have slightly different semantics for some of the return values. Consumers
             * should determine what implementations they expect to receive
             * data from and validate that they can properly handle return data from all
             * of them.
             * @param _roundId the round ID to retrieve the round data for
             * @return roundId is the round ID from the aggregator for which the data was
             * retrieved combined with a phase to ensure that round IDs get larger as
             * time moves forward.
             * @return answer is the answer for the given round
             * @return startedAt is the timestamp when the round was started.
             * (Only some AggregatorV3Interface implementations return meaningful values)
             * @return updatedAt is the timestamp when the round last was updated (i.e.
             * answer was last computed)
             * @return answeredInRound is the round ID of the round in which the answer
             * was computed.
             * (Only some AggregatorV3Interface implementations return meaningful values)
             * @dev Note that answer and updatedAt may change between queries.
             */
            function getRoundData(uint80 _roundId)
              public
              view
              checkAccess()
              override
              returns (
                uint80 roundId,
                int256 answer,
                uint256 startedAt,
                uint256 updatedAt,
                uint80 answeredInRound
              )
            {
              return super.getRoundData(_roundId);
            }
          
            /**
             * @notice get data about the latest round. Consumers are encouraged to check
             * that they're receiving fresh data by inspecting the updatedAt and
             * answeredInRound return values.
             * Note that different underlying implementations of AggregatorV3Interface
             * have slightly different semantics for some of the return values. Consumers
             * should determine what implementations they expect to receive
             * data from and validate that they can properly handle return data from all
             * of them.
             * @return roundId is the round ID from the aggregator for which the data was
             * retrieved combined with a phase to ensure that round IDs get larger as
             * time moves forward.
             * @return answer is the answer for the given round
             * @return startedAt is the timestamp when the round was started.
             * (Only some AggregatorV3Interface implementations return meaningful values)
             * @return updatedAt is the timestamp when the round last was updated (i.e.
             * answer was last computed)
             * @return answeredInRound is the round ID of the round in which the answer
             * was computed.
             * (Only some AggregatorV3Interface implementations return meaningful values)
             * @dev Note that answer and updatedAt may change between queries.
             */
            function latestRoundData()
              public
              view
              checkAccess()
              override
              returns (
                uint80 roundId,
                int256 answer,
                uint256 startedAt,
                uint256 updatedAt,
                uint80 answeredInRound
              )
            {
              return super.latestRoundData();
            }
          
            /**
             * @notice Used if an aggregator contract has been proposed.
             * @param _roundId the round ID to retrieve the round data for
             * @return roundId is the round ID for which data was retrieved
             * @return answer is the answer for the given round
             * @return startedAt is the timestamp when the round was started.
             * (Only some AggregatorV3Interface implementations return meaningful values)
             * @return updatedAt is the timestamp when the round last was updated (i.e.
             * answer was last computed)
             * @return answeredInRound is the round ID of the round in which the answer
             * was computed.
            */
            function proposedGetRoundData(uint80 _roundId)
              public
              view
              checkAccess()
              hasProposal()
              override
              returns (
                uint80 roundId,
                int256 answer,
                uint256 startedAt,
                uint256 updatedAt,
                uint80 answeredInRound
              )
            {
              return super.proposedGetRoundData(_roundId);
            }
          
            /**
             * @notice Used if an aggregator contract has been proposed.
             * @return roundId is the round ID for which data was retrieved
             * @return answer is the answer for the given round
             * @return startedAt is the timestamp when the round was started.
             * (Only some AggregatorV3Interface implementations return meaningful values)
             * @return updatedAt is the timestamp when the round last was updated (i.e.
             * answer was last computed)
             * @return answeredInRound is the round ID of the round in which the answer
             * was computed.
            */
            function proposedLatestRoundData()
              public
              view
              checkAccess()
              hasProposal()
              override
              returns (
                uint80 roundId,
                int256 answer,
                uint256 startedAt,
                uint256 updatedAt,
                uint80 answeredInRound
              )
            {
              return super.proposedLatestRoundData();
            }
          
            /**
             * @dev reverts if the caller does not have access by the accessController
             * contract or is the contract itself.
             */
            modifier checkAccess() {
              AccessControllerInterface ac = accessController;
              require(address(ac) == address(0) || ac.hasAccess(msg.sender, msg.data), "No access");
              _;
            }
          }

          File 4 of 4: AccessControlledOCR2Aggregator
          // SPDX-License-Identifier: MIT
          pragma solidity =0.8.19;
          import "./OCR2Aggregator.sol";
          import "./SimpleReadAccessController.sol";
          /**
           * @notice Wrapper of OCR2Aggregator which checks read access on Aggregator-interface methods
           */
          contract AccessControlledOCR2Aggregator is OCR2Aggregator, SimpleReadAccessController {
            constructor(
              LinkTokenInterface _link,
              int192 _minAnswer,
              int192 _maxAnswer,
              AccessControllerInterface _billingAccessController,
              AccessControllerInterface _requesterAccessController,
              uint8 _decimals,
              string memory description
            )
              OCR2Aggregator(
                _link,
                _minAnswer,
                _maxAnswer,
                _billingAccessController,
                _requesterAccessController,
                _decimals,
                description
              ) {
              }
            /*
             * Versioning
             */
            function typeAndVersion()
              external
              override
              pure
              virtual
              returns (string memory)
            {
              return "AccessControlledOCR2Aggregator 1.0.0";
            }
            /*
             * v2 Aggregator interface
             */
            /// @inheritdoc OCR2Aggregator
            function latestAnswer()
              public
              override
              view
              checkAccess()
              returns (int256)
            {
              return super.latestAnswer();
            }
            /// @inheritdoc OCR2Aggregator
            function latestTimestamp()
              public
              override
              view
              checkAccess()
              returns (uint256)
            {
              return super.latestTimestamp();
            }
            /// @inheritdoc OCR2Aggregator
            function latestRound()
              public
              override
              view
              checkAccess()
              returns (uint256)
            {
              return super.latestRound();
            }
            /// @inheritdoc OCR2Aggregator
            function getAnswer(uint256 _roundId)
              public
              override
              view
              checkAccess()
              returns (int256)
            {
              return super.getAnswer(_roundId);
            }
            /// @inheritdoc OCR2Aggregator
            function getTimestamp(uint256 _roundId)
              public
              override
              view
              checkAccess()
              returns (uint256)
            {
              return super.getTimestamp(_roundId);
            }
            /*
             * v3 Aggregator interface
             */
            /// @inheritdoc OCR2Aggregator
            function description()
              public
              override
              view
              checkAccess()
              returns (string memory)
            {
              return super.description();
            }
            /// @inheritdoc OCR2Aggregator
            function getRoundData(uint80 _roundId)
              public
              override
              view
              checkAccess()
              returns (
                uint80 roundId,
                int256 answer,
                uint256 startedAt,
                uint256 updatedAt,
                uint80 answeredInRound
              )
            {
              return super.getRoundData(_roundId);
            }
            /// @inheritdoc OCR2Aggregator
            function latestRoundData()
              public
              override
              view
              checkAccess()
              returns (
                uint80 roundId,
                int256 answer,
                uint256 startedAt,
                uint256 updatedAt,
                uint80 answeredInRound
              )
            {
              return super.latestRoundData();
            }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.0;
          import "./ConfirmedOwnerWithProposal.sol";
          /**
           * @title The ConfirmedOwner contract
           * @notice A contract with helpers for basic contract ownership.
           */
          contract ConfirmedOwner is ConfirmedOwnerWithProposal {
            constructor(
              address newOwner
            )
              ConfirmedOwnerWithProposal(
                newOwner,
                address(0)
              )
            {
            }
          }// SPDX-License-Identifier: MIT
          pragma solidity ^0.8.0;
          import "./interfaces/OwnableInterface.sol";
          /**
           * @title The ConfirmedOwner contract
           * @notice A contract with helpers for basic contract ownership.
           */
          contract ConfirmedOwnerWithProposal is OwnableInterface {
            address private s_owner;
            address private s_pendingOwner;
            event OwnershipTransferRequested(
              address indexed from,
              address indexed to
            );
            event OwnershipTransferred(
              address indexed from,
              address indexed to
            );
            constructor(
              address newOwner,
              address pendingOwner
            ) {
              require(newOwner != address(0), "Cannot set owner to zero");
              s_owner = newOwner;
              if (pendingOwner != address(0)) {
                _transferOwnership(pendingOwner);
              }
            }
            /**
             * @notice Allows an owner to begin transferring ownership to a new address,
             * pending.
             */
            function transferOwnership(
              address to
            )
              public
              override
              onlyOwner()
            {
              _transferOwnership(to);
            }
            /**
             * @notice Allows an ownership transfer to be completed by the recipient.
             */
            function acceptOwnership()
              external
              override
            {
              require(msg.sender == s_pendingOwner, "Must be proposed owner");
              address oldOwner = s_owner;
              s_owner = msg.sender;
              s_pendingOwner = address(0);
              emit OwnershipTransferred(oldOwner, msg.sender);
            }
            /**
             * @notice Get the current owner
             */
            function owner()
              public
              view
              override
              returns (
                address
              )
            {
              return s_owner;
            }
            /**
             * @notice validate, transfer ownership, and emit relevant events
             */
            function _transferOwnership(
              address to
            )
              private
            {
              require(to != msg.sender, "Cannot transfer to self");
              s_pendingOwner = to;
              emit OwnershipTransferRequested(s_owner, to);
            }
            /**
             * @notice validate access
             */
            function _validateOwnership()
              internal
              view
            {
              require(msg.sender == s_owner, "Only callable by owner");
            }
            /**
             * @notice Reverts if called by anyone other than the contract owner.
             */
            modifier onlyOwner() {
              _validateOwnership();
              _;
            }
          }// SPDX-License-Identifier: MIT
          pragma solidity ^0.8.0;
          import "./interfaces/TypeAndVersionInterface.sol";
          abstract contract OCR2Abstract is TypeAndVersionInterface {
            // Maximum number of oracles the offchain reporting protocol is designed for
            uint256 constant internal maxNumOracles = 31;
            /**
             * @notice triggers a new run of the offchain reporting protocol
             * @param previousConfigBlockNumber block in which the previous config was set, to simplify historic analysis
             * @param configDigest configDigest of this configuration
             * @param configCount ordinal number of this config setting among all config settings over the life of this contract
             * @param signers ith element is address ith oracle uses to sign a report
             * @param transmitters ith element is address ith oracle uses to transmit a report via the transmit method
             * @param f maximum number of faulty/dishonest oracles the protocol can tolerate while still working correctly
             * @param onchainConfig serialized configuration used by the contract (and possibly oracles)
             * @param offchainConfigVersion version of the serialization format used for "offchainConfig" parameter
             * @param offchainConfig serialized configuration used by the oracles exclusively and only passed through the contract
             */
            event ConfigSet(
              uint32 previousConfigBlockNumber,
              bytes32 configDigest,
              uint64 configCount,
              address[] signers,
              address[] transmitters,
              uint8 f,
              bytes onchainConfig,
              uint64 offchainConfigVersion,
              bytes offchainConfig
            );
            /**
             * @notice sets offchain reporting protocol configuration incl. participating oracles
             * @param signers addresses with which oracles sign the reports
             * @param transmitters addresses oracles use to transmit the reports
             * @param f number of faulty oracles the system can tolerate
             * @param onchainConfig serialized configuration used by the contract (and possibly oracles)
             * @param offchainConfigVersion version number for offchainEncoding schema
             * @param offchainConfig serialized configuration used by the oracles exclusively and only passed through the contract
             */
            function setConfig(
              address[] memory signers,
              address[] memory transmitters,
              uint8 f,
              bytes memory onchainConfig,
              uint64 offchainConfigVersion,
              bytes memory offchainConfig
            )
              external
              virtual;
            /**
             * @notice information about current offchain reporting protocol configuration
             * @return configCount ordinal number of current config, out of all configs applied to this contract so far
             * @return blockNumber block at which this config was set
             * @return configDigest domain-separation tag for current config (see _configDigestFromConfigData)
             */
            function latestConfigDetails()
              external
              view
              virtual
              returns (
                uint32 configCount,
                uint32 blockNumber,
                bytes32 configDigest
              );
            function _configDigestFromConfigData(
              uint256 chainId,
              address contractAddress,
              uint64 configCount,
              address[] memory signers,
              address[] memory transmitters,
              uint8 f,
              bytes memory onchainConfig,
              uint64 offchainConfigVersion,
              bytes memory offchainConfig
            )
              internal
              pure
              returns (bytes32)
            {
              uint256 h = uint256(keccak256(abi.encode(chainId, contractAddress, configCount,
                signers, transmitters, f, onchainConfig, offchainConfigVersion, offchainConfig
              )));
              uint256 prefixMask = type(uint256).max << (256-16); // 0xFFFF00..00
              uint256 prefix = 0x0001 << (256-16); // 0x000100..00
              return bytes32((prefix & prefixMask) | (h & ~prefixMask));
            }
            /**
            * @notice optionally emitted to indicate the latest configDigest and epoch for
               which a report was successfully transmitted. Alternatively, the contract may
               use latestConfigDigestAndEpoch with scanLogs set to false.
            */
            event Transmitted(
              bytes32 configDigest,
              uint32 epoch
            );
            /**
             * @notice optionally returns the latest configDigest and epoch for which a
               report was successfully transmitted. Alternatively, the contract may return
               scanLogs set to true and use Transmitted events to provide this information
               to offchain watchers.
             * @return scanLogs indicates whether to rely on the configDigest and epoch
               returned or whether to scan logs for the Transmitted event instead.
             * @return configDigest
             * @return epoch
             */
            function latestConfigDigestAndEpoch()
              external
              view
              virtual
              returns(
                bool scanLogs,
                bytes32 configDigest,
                uint32 epoch
              );
            /**
             * @notice transmit is called to post a new report to the contract
             * @param reportContext serialized report context containing configDigest, epoch, round, extraHash
             * @param report serialized report, which the signatures are signing
             * @param rs ith element is the R components of the ith signature on report. Must have at most maxNumOracles entries
             * @param ss ith element is the S components of the ith signature on report. Must have at most maxNumOracles entries
             * @param rawVs ith element is the the V component of the ith signature
             */
            function transmit(
              // NOTE: If these parameters are changed, expectedMsgDataLength and/or
              // TRANSMIT_MSGDATA_CONSTANT_LENGTH_COMPONENT need to be changed accordingly
              bytes32[3] calldata reportContext,
              bytes calldata report,
              bytes32[] calldata rs, bytes32[] calldata ss, bytes32 rawVs // signatures
            )
              external
              virtual;
          }
          // SPDX-License-Identifier: MIT
          pragma solidity =0.8.19;
          import "./interfaces/AccessControllerInterface.sol";
          import "./interfaces/AggregatorV2V3Interface.sol";
          import "./interfaces/AggregatorValidatorInterface.sol";
          import "./interfaces/LinkTokenInterface.sol";
          import "./interfaces/TypeAndVersionInterface.sol";
          import "./OCR2Abstract.sol";
          import "./OwnerIsCreator.sol";
          /**
           * @notice OCR2Aggregator for numerical data with billing support.
           * @dev
           * If you read or change this, be sure to read or adjust the comments. They
           * track the units of the values under consideration, and are crucial to
           * the readability of the operations it specifies.
           * @notice
           * Billing Trust Model:
           * Nothing in this contract prevents a billing admin from setting insane
           * values for the billing parameters in setBilling. Oracles
           * participating in this contract should regularly check that the
           * parameters make sense. Similarly, the outstanding obligations of this
           * contract to the oracles can exceed the funds held by the contract.
           * Oracles participating in this contract should regularly check that it
           * holds sufficient funds and stop interacting with it if funding runs
           * out.
           * This still leaves oracles with some risk due to TOCTOU issues.
           * However, since the sums involved are pretty small (Ethereum
           * transactions aren't that expensive in the end) and an oracle would
           * likely stop participating in a contract it repeatedly lost money on,
           * this risk is deemed acceptable. Oracles should also regularly
           * withdraw any funds in the contract to prevent issues where the
           * contract becomes underfunded at a later time, and different oracles
           * are competing for the left-over funds.
           * Finally, note that any change to the set of oracles or to the billing
           * parameters will trigger payout of all oracles first (using the old
           * parameters), a billing admin cannot take away funds that are already
           * marked for payment.
           */
          contract OCR2Aggregator is OCR2Abstract, OwnerIsCreator, AggregatorV2V3Interface {
            // This contract is divided into sections. Each section defines a set of
            // variables, events, and functions that belong together.
            /***************************************************************************
             * Section: Variables used in multiple other sections
             **************************************************************************/
            struct Transmitter {
              bool active;
              // Index of oracle in s_signersList/s_transmittersList
              uint8 index;
              // juels-denominated payment for transmitters, covering gas costs incurred
              // by the transmitter plus additional rewards. The entire LINK supply (1e9
              // LINK = 1e27 Juels) will always fit into a uint96.
              uint96 paymentJuels;
            }
            mapping (address /* transmitter address */ => Transmitter) internal s_transmitters;
            struct Signer {
              bool active;
              // Index of oracle in s_signersList/s_transmittersList
              uint8 index;
            }
            mapping (address /* signer address */ => Signer) internal s_signers;
            // s_signersList contains the signing address of each oracle
            address[] internal s_signersList;
            // s_transmittersList contains the transmission address of each oracle,
            // i.e. the address the oracle actually sends transactions to the contract from
            address[] internal s_transmittersList;
            // We assume that all oracles contribute observations to all rounds. this
            // variable tracks (per-oracle) from what round an oracle should be rewarded,
            // i.e. the oracle gets (latestAggregatorRoundId -
            // rewardFromAggregatorRoundId) * reward
            uint32[maxNumOracles] internal s_rewardFromAggregatorRoundId;
            bytes32 s_latestConfigDigest;
            // Storing these fields used on the hot path in a HotVars variable reduces the
            // retrieval of all of them to a single SLOAD.
            struct HotVars {
              // maximum number of faulty oracles
              uint8 f;
              // epoch and round from OCR protocol.
              // 32 most sig bits for epoch, 8 least sig bits for round
              uint40 latestEpochAndRound;
              // Chainlink Aggregators expose a roundId to consumers. The offchain reporting
              // protocol does not use this id anywhere. We increment it whenever a new
              // transmission is made to provide callers with contiguous ids for successive
              // reports.
              uint32 latestAggregatorRoundId;
              // Highest compensated gas price, in gwei uints
              uint32 maximumGasPriceGwei;
              // If gas price is less (in gwei units), transmitter gets half the savings
              uint32 reasonableGasPriceGwei;
              // Fixed LINK reward for each observer
              uint32 observationPaymentGjuels;
              // Fixed reward for transmitter
              uint32 transmissionPaymentGjuels;
              // Overhead incurred by accounting logic
              uint24 accountingGas;
            }
            HotVars internal s_hotVars;
            // Transmission records the median answer from the transmit transaction at
            // time timestamp
            struct Transmission {
              int192 answer; // 192 bits ought to be enough for anyone
              uint32 observationsTimestamp; // when were observations made offchain
              uint32 transmissionTimestamp; // when was report received onchain
            }
            mapping(uint32 /* aggregator round ID */ => Transmission) internal s_transmissions;
            // Lowest answer the system is allowed to report in response to transmissions
            int192 immutable public minAnswer;
            // Highest answer the system is allowed to report in response to transmissions
            int192 immutable public maxAnswer;
            /***************************************************************************
             * Section: Constructor
             **************************************************************************/
            /**
             * @param link address of the LINK contract
             * @param minAnswer_ lowest answer the median of a report is allowed to be
             * @param maxAnswer_ highest answer the median of a report is allowed to be
             * @param requesterAccessController access controller for requesting new rounds
             * @param decimals_ answers are stored in fixed-point format, with this many digits of precision
             * @param description_ short human-readable description of observable this contract's answers pertain to
             */
            constructor(
              LinkTokenInterface link,
              int192 minAnswer_,
              int192 maxAnswer_,
              AccessControllerInterface billingAccessController,
              AccessControllerInterface requesterAccessController,
              uint8 decimals_,
              string memory description_
            ) {
              s_linkToken = link;
              emit LinkTokenSet(LinkTokenInterface(address(0)), link);
              _setBillingAccessController(billingAccessController);
              decimals = decimals_;
              s_description = description_;
              setRequesterAccessController(requesterAccessController);
              setValidatorConfig(AggregatorValidatorInterface(address(0x0)), 0);
              minAnswer = minAnswer_;
              maxAnswer = maxAnswer_;
            }
            /***************************************************************************
             * Section: OCR2Abstract Configuration
             **************************************************************************/
            // incremented each time a new config is posted. This count is incorporated
            // into the config digest to prevent replay attacks.
            uint32 internal s_configCount;
            // makes it easier for offchain systems to extract config from logs
            uint32 internal s_latestConfigBlockNumber;
            // left as a function so this check can be disabled in derived contracts
            function _requirePositiveF (
              uint256 f
            )
              internal
              pure
              virtual
            {
              require(0 < f, "f must be positive");
            }
            struct SetConfigArgs {
              address[] signers;
              address[] transmitters;
              uint8 f;
              bytes onchainConfig;
              uint64 offchainConfigVersion;
              bytes offchainConfig;
            }
            /// @inheritdoc OCR2Abstract
            function setConfig(
              address[] memory signers,
              address[] memory transmitters,
              uint8 f,
              bytes memory onchainConfig,
              uint64 offchainConfigVersion,
              bytes memory offchainConfig
            )
              external
              override
              onlyOwner()
            {
              require(signers.length <= maxNumOracles, "too many oracles");
              require(signers.length == transmitters.length, "oracle length mismatch");
              require(3*f < signers.length, "faulty-oracle f too high");
              _requirePositiveF(f);
              require(keccak256(onchainConfig) == keccak256(abi.encodePacked(uint8(1) /*version*/, minAnswer, maxAnswer)), "invalid onchainConfig");
              SetConfigArgs memory args = SetConfigArgs({
                signers: signers,
                transmitters: transmitters,
                f: f,
                onchainConfig: onchainConfig,
                offchainConfigVersion: offchainConfigVersion,
                offchainConfig: offchainConfig
              });
              s_hotVars.latestEpochAndRound = 0;
              _payOracles();
              // remove any old signer/transmitter addresses
              uint256 oldLength = s_signersList.length;
              for (uint256 i = 0; i < oldLength; i++) {
                address signer = s_signersList[i];
                address transmitter = s_transmittersList[i];
                delete s_signers[signer];
                delete s_transmitters[transmitter];
              }
              delete s_signersList;
              delete s_transmittersList;
              // add new signer/transmitter addresses
              for (uint i = 0; i < args.signers.length; i++) {
                require(
                  !s_signers[args.signers[i]].active,
                  "repeated signer address"
                );
                s_signers[args.signers[i]] = Signer({
                  active: true,
                  index: uint8(i)
                });
                require(
                  !s_transmitters[args.transmitters[i]].active,
                  "repeated transmitter address"
                );
                s_transmitters[args.transmitters[i]] = Transmitter({
                  active: true,
                  index: uint8(i),
                  paymentJuels: 0
                });
              }
              s_signersList = args.signers;
              s_transmittersList = args.transmitters;
              s_hotVars.f = args.f;
              uint32 previousConfigBlockNumber = s_latestConfigBlockNumber;
              s_latestConfigBlockNumber = uint32(block.number);
              s_configCount += 1;
              s_latestConfigDigest = _configDigestFromConfigData(
                block.chainid,
                address(this),
                s_configCount,
                args.signers,
                args.transmitters,
                args.f,
                args.onchainConfig,
                args.offchainConfigVersion,
                args.offchainConfig
              );
              emit ConfigSet(
                previousConfigBlockNumber,
                s_latestConfigDigest,
                s_configCount,
                args.signers,
                args.transmitters,
                args.f,
                args.onchainConfig,
                args.offchainConfigVersion,
                args.offchainConfig
              );
              uint32 latestAggregatorRoundId = s_hotVars.latestAggregatorRoundId;
              for (uint256 i = 0; i < args.signers.length; i++) {
                s_rewardFromAggregatorRoundId[i] = latestAggregatorRoundId;
              }
            }
            /// @inheritdoc OCR2Abstract
            function latestConfigDetails()
              external
              override
              view
              returns (
                uint32 configCount,
                uint32 blockNumber,
                bytes32 configDigest
              )
            {
              return (s_configCount, s_latestConfigBlockNumber, s_latestConfigDigest);
            }
            /**
             * @return list of addresses permitted to transmit reports to this contract
             * @dev The list will match the order used to specify the transmitter during setConfig
             */
            function getTransmitters()
              external
              view
              returns(address[] memory)
            {
              return s_transmittersList;
            }
            /***************************************************************************
             * Section: Onchain Validation
             **************************************************************************/
            // Configuration for validator
            struct ValidatorConfig {
              AggregatorValidatorInterface validator;
              uint32 gasLimit;
            }
            ValidatorConfig private s_validatorConfig;
            /**
             * @notice indicates that the validator configuration has been set
             * @param previousValidator previous validator contract
             * @param previousGasLimit previous gas limit for validate calls
             * @param currentValidator current validator contract
             * @param currentGasLimit current gas limit for validate calls
             */
            event ValidatorConfigSet(
              AggregatorValidatorInterface indexed previousValidator,
              uint32 previousGasLimit,
              AggregatorValidatorInterface indexed currentValidator,
              uint32 currentGasLimit
            );
            /**
             * @notice validator configuration
             * @return validator validator contract
             * @return gasLimit gas limit for validate calls
             */
            function getValidatorConfig()
              external
              view
              returns (AggregatorValidatorInterface validator, uint32 gasLimit)
            {
              ValidatorConfig memory vc = s_validatorConfig;
              return (vc.validator, vc.gasLimit);
            }
            /**
             * @notice sets validator configuration
             * @dev set newValidator to 0x0 to disable validate calls
             * @param newValidator address of the new validator contract
             * @param newGasLimit new gas limit for validate calls
             */
            function setValidatorConfig(
              AggregatorValidatorInterface newValidator,
              uint32 newGasLimit
            )
              public
              onlyOwner()
            {
              ValidatorConfig memory previous = s_validatorConfig;
              if (previous.validator != newValidator || previous.gasLimit != newGasLimit) {
                s_validatorConfig = ValidatorConfig({
                  validator: newValidator,
                  gasLimit: newGasLimit
                });
                emit ValidatorConfigSet(previous.validator, previous.gasLimit, newValidator, newGasLimit);
              }
            }
            function _validateAnswer(
              uint32 aggregatorRoundId,
              int256 answer
            )
              private
            {
              ValidatorConfig memory vc = s_validatorConfig;
              if (address(vc.validator) == address(0)) {
                return;
              }
              uint32 prevAggregatorRoundId = aggregatorRoundId - 1;
              int256 prevAggregatorRoundAnswer = s_transmissions[prevAggregatorRoundId].answer;
              require(
                _callWithExactGasEvenIfTargetIsNoContract(
                  vc.gasLimit,
                  address(vc.validator),
                  abi.encodeWithSignature(
                    "validate(uint256,int256,uint256,int256)",
                    uint256(prevAggregatorRoundId),
                    prevAggregatorRoundAnswer,
                    uint256(aggregatorRoundId),
                    answer
                  )
                ),
                "insufficient gas"
              );
            }
            uint256 private constant CALL_WITH_EXACT_GAS_CUSHION = 5_000;
            /**
             * @dev calls target address with exactly gasAmount gas and data as calldata
             * or reverts if at least gasAmount gas is not available.
             */
            function _callWithExactGasEvenIfTargetIsNoContract(
              uint256 gasAmount,
              address target,
              bytes memory data
            )
              private
              returns (bool sufficientGas)
            {
              // solhint-disable-next-line no-inline-assembly
              assembly {
                let g := gas()
                // Compute g -= CALL_WITH_EXACT_GAS_CUSHION and check for underflow. We
                // need the cushion since the logic following the above call to gas also
                // costs gas which we cannot account for exactly. So cushion is a
                // conservative upper bound for the cost of this logic.
                if iszero(lt(g, CALL_WITH_EXACT_GAS_CUSHION)) {
                  g := sub(g, CALL_WITH_EXACT_GAS_CUSHION)
                  // If g - g//64 <= gasAmount, we don't have enough gas. (We subtract g//64
                  // because of EIP-150.)
                  if gt(sub(g, div(g, 64)), gasAmount) {
                    // Call and ignore success/return data. Note that we did not check
                    // whether a contract actually exists at the target address.
                    pop(call(gasAmount, target, 0, add(data, 0x20), mload(data), 0, 0))
                    sufficientGas := true
                  }
                }
              }
            }
            /***************************************************************************
             * Section: RequestNewRound
             **************************************************************************/
            AccessControllerInterface internal s_requesterAccessController;
            /**
             * @notice emitted when a new requester access controller contract is set
             * @param old the address prior to the current setting
             * @param current the address of the new access controller contract
             */
            event RequesterAccessControllerSet(AccessControllerInterface old, AccessControllerInterface current);
            /**
             * @notice emitted to immediately request a new round
             * @param requester the address of the requester
             * @param configDigest the latest transmission's configDigest
             * @param epoch the latest transmission's epoch
             * @param round the latest transmission's round
             */
            event RoundRequested(address indexed requester, bytes32 configDigest, uint32 epoch, uint8 round);
            /**
             * @notice address of the requester access controller contract
             * @return requester access controller address
             */
            function getRequesterAccessController()
              external
              view
              returns (AccessControllerInterface)
            {
              return s_requesterAccessController;
            }
            /**
             * @notice sets the requester access controller
             * @param requesterAccessController designates the address of the new requester access controller
             */
            function setRequesterAccessController(AccessControllerInterface requesterAccessController)
              public
              onlyOwner()
            {
              AccessControllerInterface oldController = s_requesterAccessController;
              if (requesterAccessController != oldController) {
                s_requesterAccessController = AccessControllerInterface(requesterAccessController);
                emit RequesterAccessControllerSet(oldController, requesterAccessController);
              }
            }
            /**
             * @notice immediately requests a new round
             * @return the aggregatorRoundId of the next round. Note: The report for this round may have been
             * transmitted (but not yet mined) *before* requestNewRound() was even called. There is *no*
             * guarantee of causality between the request and the report at aggregatorRoundId.
             */
            function requestNewRound() external returns (uint80) {
              require(msg.sender == owner() || s_requesterAccessController.hasAccess(msg.sender, msg.data),
                "Only owner&requester can call");
              uint40 latestEpochAndRound = s_hotVars.latestEpochAndRound;
              uint32 latestAggregatorRoundId = s_hotVars.latestAggregatorRoundId;
              emit RoundRequested(
                msg.sender,
                s_latestConfigDigest,
                uint32(latestEpochAndRound >> 8),
                uint8(latestEpochAndRound)
              );
              return latestAggregatorRoundId + 1;
            }
            /***************************************************************************
             * Section: Transmission
             **************************************************************************/
            /**
             * @notice indicates that a new report was transmitted
             * @param aggregatorRoundId the round to which this report was assigned
             * @param answer median of the observations attached to this report
             * @param transmitter address from which the report was transmitted
             * @param observationsTimestamp when were observations made offchain
             * @param observations observations transmitted with this report
             * @param observers i-th element is the oracle id of the oracle that made the i-th observation
             * @param juelsPerFeeCoin exchange rate between feeCoin (e.g. ETH on Ethereum) and LINK, denominated in juels
             * @param configDigest configDigest of transmission
             * @param epochAndRound least-significant byte is the OCR protocol round number, the other bytes give the big-endian OCR protocol epoch number
             */
            event NewTransmission(
              uint32 indexed aggregatorRoundId,
              int192 answer,
              address transmitter,
              uint32 observationsTimestamp,
              int192[] observations,
              bytes observers,
              int192 juelsPerFeeCoin,
              bytes32 configDigest,
              uint40 epochAndRound
            );
            // Used to relieve stack pressure in transmit
            struct Report {
              uint32 observationsTimestamp;
              bytes observers; // ith element is the index of the ith observer
              int192[] observations; // ith element is the ith observation
              int192 juelsPerFeeCoin;
            }
            // _decodeReport decodes a serialized report into a Report struct
            function _decodeReport(bytes memory rawReport)
              internal
              pure
              returns (
                Report memory
              )
            {
              uint32 observationsTimestamp;
              bytes32 rawObservers;
              int192[] memory observations;
              int192 juelsPerFeeCoin;
              (observationsTimestamp, rawObservers, observations, juelsPerFeeCoin) = abi.decode(rawReport, (uint32, bytes32, int192[], int192));
              _requireExpectedReportLength(rawReport, observations);
              uint256 numObservations = observations.length;
              bytes memory observers = abi.encodePacked(rawObservers);
              assembly {
                // we truncate observers from length 32 to the number of observations
                mstore(observers, numObservations)
              }
              return Report({
                observationsTimestamp: observationsTimestamp,
                observers: observers,
                observations: observations,
                juelsPerFeeCoin: juelsPerFeeCoin
              });
            }
            // The constant-length components of the msg.data sent to transmit.
            // See the "If we wanted to call sam" example on for example reasoning
            // https://solidity.readthedocs.io/en/v0.7.2/abi-spec.html
            uint256 private constant TRANSMIT_MSGDATA_CONSTANT_LENGTH_COMPONENT =
              4 + // function selector
              32 * 3 + // 3 words containing reportContext
              32 + // word containing start location of abiencoded report value
              32 + // word containing start location of abiencoded rs value
              32 + // word containing start location of abiencoded ss value
              32 + // rawVs value
              32 + // word containing length of report
              32 + // word containing length rs
              32 + // word containing length of ss
              0; // placeholder
            // Make sure the calldata length matches the inputs. Otherwise, the
            // transmitter could append an arbitrarily long (up to gas-block limit)
            // string of 0 bytes, which we would reimburse at a rate of 16 gas/byte, but
            // which would only cost the transmitter 4 gas/byte.
            function _requireExpectedMsgDataLength(
              bytes calldata report,
              bytes32[] calldata rs,
              bytes32[] calldata ss
            )
              private
              pure
            {
              // calldata will never be big enough to make this overflow
              uint256 expected = TRANSMIT_MSGDATA_CONSTANT_LENGTH_COMPONENT +
                report.length + // one byte per entry in report
                rs.length * 32 + // 32 bytes per entry in rs
                ss.length * 32 + // 32 bytes per entry in ss
                0; // placeholder
              require(msg.data.length == expected, "calldata length mismatch");
            }
            /// @inheritdoc OCR2Abstract
            function transmit(
              // reportContext consists of:
              // reportContext[0]: ConfigDigest
              // reportContext[1]: 27 byte padding, 4-byte epoch and 1-byte round
              // reportContext[2]: ExtraHash
              bytes32[3] calldata reportContext,
              bytes calldata report,
              // ECDSA signatures
              bytes32[] calldata rs,
              bytes32[] calldata ss,
              bytes32 rawVs
            )
              external
              override
            {
              // NOTE: If the arguments to this function are changed, _requireExpectedMsgDataLength and/or
              // TRANSMIT_MSGDATA_CONSTANT_LENGTH_COMPONENT need to be changed accordingly
              uint256 initialGas = gasleft(); // This line must come first
              HotVars memory hotVars = s_hotVars;
              uint40 epochAndRound = uint40(uint256(reportContext[1]));
              require(hotVars.latestEpochAndRound < epochAndRound, "stale report");
              require(s_transmitters[msg.sender].active, "unauthorized transmitter");
              require(s_latestConfigDigest == reportContext[0], "configDigest mismatch");
              _requireExpectedMsgDataLength(report, rs, ss);
              require(rs.length == hotVars.f + 1, "wrong number of signatures");
              require(rs.length == ss.length, "signatures out of registration");
              // Verify signatures attached to report
              {
                bytes32 h = keccak256(abi.encode(keccak256(report), reportContext));
                // i-th byte counts number of sigs made by i-th signer
                uint256 signedCount = 0;
                Signer memory signer;
                for (uint i = 0; i < rs.length; i++) {
                  address signerAddress = ecrecover(h, uint8(rawVs[i])+27, rs[i], ss[i]);
                  signer = s_signers[signerAddress];
                  require(signer.active, "signature error");
                  unchecked{
                    signedCount += 1 << (8 * signer.index);
                  }
                }
                // The first byte of the mask can be 0, because we only ever have 31 oracles
                require(signedCount & 0x0001010101010101010101010101010101010101010101010101010101010101 == signedCount, "duplicate signer");
              }
              int192 juelsPerFeeCoin = _report(hotVars, reportContext[0], epochAndRound, report);
              _payTransmitter(hotVars, juelsPerFeeCoin, uint32(initialGas), msg.sender);
            }
            /**
             * @notice details about the most recent report
             * @return configDigest domain separation tag for the latest report
             * @return epoch epoch in which the latest report was generated
             * @return round OCR round in which the latest report was generated
             * @return latestAnswer_ median value from latest report
             * @return latestTimestamp_ when the latest report was transmitted
             */
            function latestTransmissionDetails()
              external
              view
              returns (
                bytes32 configDigest,
                uint32 epoch,
                uint8 round,
                int192 latestAnswer_,
                uint64 latestTimestamp_
              )
            {
              require(msg.sender == tx.origin, "Only callable by EOA");
              return (
                s_latestConfigDigest,
                uint32(s_hotVars.latestEpochAndRound >> 8),
                uint8(s_hotVars.latestEpochAndRound),
                s_transmissions[s_hotVars.latestAggregatorRoundId].answer,
                s_transmissions[s_hotVars.latestAggregatorRoundId].transmissionTimestamp
              );
            }
            /// @inheritdoc OCR2Abstract
            function latestConfigDigestAndEpoch()
              external
              override
              view
              virtual
              returns(
                bool scanLogs,
                bytes32 configDigest,
                uint32 epoch
              )
            {
              return (false, s_latestConfigDigest, uint32(s_hotVars.latestEpochAndRound >> 8));
            }
            function _requireExpectedReportLength(
              bytes memory report,
              int192[] memory observations
            )
              private
              pure
            {
              uint256 expected =
                32 + // observationsTimestamp
                32 + // rawObservers
                32 + // observations offset
                32 + // juelsPerFeeCoin
                32 + // observations length
                32 * observations.length + // observations payload
                0;
              require(report.length == expected, "report length mismatch");
            }
            function _report(
              HotVars memory hotVars,
              bytes32 configDigest,
              uint40 epochAndRound,
              bytes memory rawReport
            )
              internal
              returns (int192 juelsPerFeeCoin)
            {
              Report memory report = _decodeReport(rawReport);
              require(report.observations.length <= maxNumOracles, "num observations out of bounds");
              // Offchain logic ensures that a quorum of oracles is operating on a matching set of at least
              // 2f+1 observations. By assumption, up to f of those can be faulty, which includes being
              // malformed. Conversely, more than f observations have to be well-formed and sent on chain.
              require(hotVars.f < report.observations.length, "too few values to trust median");
              hotVars.latestEpochAndRound = epochAndRound;
              // get median, validate its range, store it in new aggregator round
              int192 median = report.observations[report.observations.length/2];
              require(minAnswer <= median && median <= maxAnswer, "median is out of min-max range");
              hotVars.latestAggregatorRoundId++;
              s_transmissions[hotVars.latestAggregatorRoundId] =
                Transmission({
                  answer: median,
                  observationsTimestamp: report.observationsTimestamp,
                  transmissionTimestamp: uint32(block.timestamp)
                });
              // persist updates to hotVars
              s_hotVars = hotVars;
              emit NewTransmission(
                hotVars.latestAggregatorRoundId,
                median,
                msg.sender,
                report.observationsTimestamp,
                report.observations,
                report.observers,
                report.juelsPerFeeCoin,
                configDigest,
                epochAndRound
              );
              // Emit these for backwards compatibility with offchain consumers
              // that only support legacy events
              emit NewRound(
                hotVars.latestAggregatorRoundId,
                address(0x0), // use zero address since we don't have anybody "starting" the round here
                report.observationsTimestamp
              );
              emit AnswerUpdated(
                median,
                hotVars.latestAggregatorRoundId,
                block.timestamp
              );
              _validateAnswer(hotVars.latestAggregatorRoundId, median);
              return report.juelsPerFeeCoin;
            }
            /***************************************************************************
             * Section: v2 AggregatorInterface
             **************************************************************************/
            /**
             * @notice median from the most recent report
             */
            function latestAnswer()
              public
              override
              view
              virtual
              returns (int256)
            {
              return s_transmissions[s_hotVars.latestAggregatorRoundId].answer;
            }
            /**
             * @notice timestamp of block in which last report was transmitted
             */
            function latestTimestamp()
              public
              override
              view
              virtual
              returns (uint256)
            {
              return s_transmissions[s_hotVars.latestAggregatorRoundId].transmissionTimestamp;
            }
            /**
             * @notice Aggregator round (NOT OCR round) in which last report was transmitted
             */
            function latestRound()
              public
              override
              view
              virtual
              returns (uint256)
            {
              return s_hotVars.latestAggregatorRoundId;
            }
            /**
             * @notice median of report from given aggregator round (NOT OCR round)
             * @param roundId the aggregator round of the target report
             */
            function getAnswer(uint256 roundId)
              public
              override
              view
              virtual
              returns (int256)
            {
              if (roundId > 0xFFFFFFFF) { return 0; }
              return s_transmissions[uint32(roundId)].answer;
            }
            /**
             * @notice timestamp of block in which report from given aggregator round was transmitted
             * @param roundId aggregator round (NOT OCR round) of target report
             */
            function getTimestamp(uint256 roundId)
              public
              override
              view
              virtual
              returns (uint256)
            {
              if (roundId > 0xFFFFFFFF) { return 0; }
              return s_transmissions[uint32(roundId)].transmissionTimestamp;
            }
            /***************************************************************************
             * Section: v3 AggregatorInterface
             **************************************************************************/
            /**
             * @return answers are stored in fixed-point format, with this many digits of precision
             */
            uint8 immutable public override decimals;
            /**
             * @notice aggregator contract version
             */
            uint256 constant public override version = 6;
            string internal s_description;
            /**
             * @notice human-readable description of observable this contract is reporting on
             */
            function description()
              public
              override
              view
              virtual
              returns (string memory)
            {
              return s_description;
            }
            /**
             * @notice details for the given aggregator round
             * @param roundId target aggregator round (NOT OCR round). Must fit in uint32
             * @return roundId_ roundId
             * @return answer median of report from given roundId
             * @return startedAt timestamp of when observations were made offchain
             * @return updatedAt timestamp of block in which report from given roundId was transmitted
             * @return answeredInRound roundId
             */
            function getRoundData(uint80 roundId)
              public
              override
              view
              virtual
              returns (
                uint80 roundId_,
                int256 answer,
                uint256 startedAt,
                uint256 updatedAt,
                uint80 answeredInRound
              )
            {
              if(roundId > type(uint32).max) { return (0, 0, 0, 0, 0); }
              Transmission memory transmission = s_transmissions[uint32(roundId)];
              return (
                roundId,
                transmission.answer,
                transmission.observationsTimestamp,
                transmission.transmissionTimestamp,
                roundId
              );
            }
            /**
             * @notice aggregator details for the most recently transmitted report
             * @return roundId aggregator round of latest report (NOT OCR round)
             * @return answer median of latest report
             * @return startedAt timestamp of when observations were made offchain
             * @return updatedAt timestamp of block containing latest report
             * @return answeredInRound aggregator round of latest report
             */
            function latestRoundData()
              public
              override
              view
              virtual
              returns (
                uint80 roundId,
                int256 answer,
                uint256 startedAt,
                uint256 updatedAt,
                uint80 answeredInRound
              )
            {
              uint32 latestAggregatorRoundId = s_hotVars.latestAggregatorRoundId;
              Transmission memory transmission = s_transmissions[latestAggregatorRoundId];
              return (
                latestAggregatorRoundId,
                transmission.answer,
                transmission.observationsTimestamp,
                transmission.transmissionTimestamp,
                latestAggregatorRoundId
              );
            }
            /***************************************************************************
             * Section: Configurable LINK Token
             **************************************************************************/
            // We assume that the token contract is correct. This contract is not written
            // to handle misbehaving ERC20 tokens!
            LinkTokenInterface internal s_linkToken;
            /*
             * @notice emitted when the LINK token contract is set
             * @param oldLinkToken the address of the old LINK token contract
             * @param newLinkToken the address of the new LINK token contract
             */
            event LinkTokenSet(
              LinkTokenInterface indexed oldLinkToken,
              LinkTokenInterface indexed newLinkToken
            );
            /**
             * @notice sets the LINK token contract used for paying oracles
             * @param linkToken the address of the LINK token contract
             * @param recipient remaining funds from the previous token contract are transferred
             * here
             * @dev this function will return early (without an error) without changing any state
             * if linkToken equals getLinkToken().
             * @dev this will trigger a payout so that a malicious owner cannot take from oracles
             * what is already owed to them.
             * @dev we assume that the token contract is correct. This contract is not written
             * to handle misbehaving ERC20 tokens!
             */
            function setLinkToken(
              LinkTokenInterface linkToken,
              address recipient
            ) external
              onlyOwner()
            {
              LinkTokenInterface oldLinkToken = s_linkToken;
              if (linkToken == oldLinkToken) {
                // No change, nothing to be done
                return;
              }
              // call balanceOf as a sanity check on whether we're talking to a token
              // contract
              linkToken.balanceOf(address(this));
              // we break CEI here, but that's okay because we're dealing with a correct
              // token contract (by assumption).
              _payOracles();
              uint256 remainingBalance = oldLinkToken.balanceOf(address(this));
              require(oldLinkToken.transfer(recipient, remainingBalance), "transfer remaining funds failed");
              s_linkToken = linkToken;
              emit LinkTokenSet(oldLinkToken, linkToken);
            }
            /*
             * @notice gets the LINK token contract used for paying oracles
             * @return linkToken the address of the LINK token contract
             */
            function getLinkToken()
              external
              view
              returns(LinkTokenInterface linkToken)
            {
              return s_linkToken;
            }
            /***************************************************************************
             * Section: BillingAccessController Management
             **************************************************************************/
            // Controls who can change billing parameters. A billingAdmin is not able to
            // affect any OCR protocol settings and therefore cannot tamper with the
            // liveness or integrity of a data feed. However, a billingAdmin can set
            // faulty billing parameters causing oracles to be underpaid, or causing them
            // to be paid so much that further calls to setConfig, setBilling,
            // setLinkToken will always fail due to the contract being underfunded.
            AccessControllerInterface internal s_billingAccessController;
            /**
             * @notice emitted when a new access-control contract is set
             * @param old the address prior to the current setting
             * @param current the address of the new access-control contract
             */
            event BillingAccessControllerSet(AccessControllerInterface old, AccessControllerInterface current);
            function _setBillingAccessController(AccessControllerInterface billingAccessController)
              internal
            {
              AccessControllerInterface oldController = s_billingAccessController;
              if (billingAccessController != oldController) {
                s_billingAccessController = billingAccessController;
                emit BillingAccessControllerSet(
                  oldController,
                  billingAccessController
                );
              }
            }
            /**
             * @notice sets billingAccessController
             * @param _billingAccessController new billingAccessController contract address
             * @dev only owner can call this
             */
            function setBillingAccessController(AccessControllerInterface _billingAccessController)
              external
              onlyOwner
            {
              _setBillingAccessController(_billingAccessController);
            }
            /**
             * @notice gets billingAccessController
             * @return address of billingAccessController contract
             */
            function getBillingAccessController()
              external
              view
              returns (AccessControllerInterface)
            {
              return s_billingAccessController;
            }
            /***************************************************************************
             * Section: Billing Configuration
             **************************************************************************/
            /**
             * @notice emitted when billing parameters are set
             * @param maximumGasPriceGwei highest gas price for which transmitter will be compensated
             * @param reasonableGasPriceGwei transmitter will receive reward for gas prices under this value
             * @param observationPaymentGjuels reward to oracle for contributing an observation to a successfully transmitted report
             * @param transmissionPaymentGjuels reward to transmitter of a successful report
             * @param accountingGas gas overhead incurred by accounting logic
             */
            event BillingSet(
              uint32 maximumGasPriceGwei,
              uint32 reasonableGasPriceGwei,
              uint32 observationPaymentGjuels,
              uint32 transmissionPaymentGjuels,
              uint24 accountingGas
            );
            /**
             * @notice sets billing parameters
             * @param maximumGasPriceGwei highest gas price for which transmitter will be compensated
             * @param reasonableGasPriceGwei transmitter will receive reward for gas prices under this value
             * @param observationPaymentGjuels reward to oracle for contributing an observation to a successfully transmitted report
             * @param transmissionPaymentGjuels reward to transmitter of a successful report
             * @param accountingGas gas overhead incurred by accounting logic
             * @dev access control provided by billingAccessController
             */
            function setBilling(
              uint32 maximumGasPriceGwei,
              uint32 reasonableGasPriceGwei,
              uint32 observationPaymentGjuels,
              uint32 transmissionPaymentGjuels,
              uint24 accountingGas
            )
              external
            {
              AccessControllerInterface access = s_billingAccessController;
              require(msg.sender == owner() || access.hasAccess(msg.sender, msg.data),
                "Only owner&billingAdmin can call");
              _payOracles();
              s_hotVars.maximumGasPriceGwei = maximumGasPriceGwei;
              s_hotVars.reasonableGasPriceGwei = reasonableGasPriceGwei;
              s_hotVars.observationPaymentGjuels = observationPaymentGjuels;
              s_hotVars.transmissionPaymentGjuels = transmissionPaymentGjuels;
              s_hotVars.accountingGas = accountingGas;
              emit BillingSet(maximumGasPriceGwei, reasonableGasPriceGwei,
                observationPaymentGjuels, transmissionPaymentGjuels, accountingGas);
            }
            /**
             * @notice gets billing parameters
             * @param maximumGasPriceGwei highest gas price for which transmitter will be compensated
             * @param reasonableGasPriceGwei transmitter will receive reward for gas prices under this value
             * @param observationPaymentGjuels reward to oracle for contributing an observation to a successfully transmitted report
             * @param transmissionPaymentGjuels reward to transmitter of a successful report
             * @param accountingGas gas overhead of the accounting logic
             */
            function getBilling()
              external
              view
              returns (
                uint32 maximumGasPriceGwei,
                uint32 reasonableGasPriceGwei,
                uint32 observationPaymentGjuels,
                uint32 transmissionPaymentGjuels,
                uint24 accountingGas
              )
            {
              return (
                s_hotVars.maximumGasPriceGwei,
                s_hotVars.reasonableGasPriceGwei,
                s_hotVars.observationPaymentGjuels,
                s_hotVars.transmissionPaymentGjuels,
                s_hotVars.accountingGas
              );
            }
            /***************************************************************************
             * Section: Payments and Withdrawals
             **************************************************************************/
            /**
             * @notice withdraws an oracle's payment from the contract
             * @param transmitter the transmitter address of the oracle
             * @dev must be called by oracle's payee address
             */
            function withdrawPayment(address transmitter)
              external
            {
              require(msg.sender == s_payees[transmitter], "Only payee can withdraw");
              _payOracle(transmitter);
            }
            /**
             * @notice query an oracle's payment amount, denominated in juels
             * @param transmitterAddress the transmitter address of the oracle
             */
            function owedPayment(address transmitterAddress)
              public
              view
              returns (uint256)
            {
              Transmitter memory transmitter = s_transmitters[transmitterAddress];
              if (!transmitter.active) { return 0; }
              // safe from overflow:
              // s_hotVars.latestAggregatorRoundId - s_rewardFromAggregatorRoundId[transmitter.index] <= 2**32
              // s_hotVars.observationPaymentGjuels <= 2**32
              // 1 gwei <= 2**32
              // hence juelsAmount <= 2**96
              uint256 juelsAmount =
                uint256(s_hotVars.latestAggregatorRoundId - s_rewardFromAggregatorRoundId[transmitter.index]) *
                uint256(s_hotVars.observationPaymentGjuels) *
                (1 gwei);
              juelsAmount += transmitter.paymentJuels;
              return juelsAmount;
            }
            /**
             * @notice emitted when an oracle has been paid LINK
             * @param transmitter address from which the oracle sends reports to the transmit method
             * @param payee address to which the payment is sent
             * @param amount amount of LINK sent
             * @param linkToken address of the LINK token contract
             */
            event OraclePaid(
              address indexed transmitter,
              address indexed payee,
              uint256 amount,
              LinkTokenInterface indexed linkToken
            );
            // _payOracle pays out transmitter's balance to the corresponding payee, and zeros it out
            function _payOracle(address transmitterAddress)
              internal
            {
              Transmitter memory transmitter = s_transmitters[transmitterAddress];
              if (!transmitter.active) { return; }
              uint256 juelsAmount = owedPayment(transmitterAddress);
              if (juelsAmount > 0) {
                address payee = s_payees[transmitterAddress];
                // Poses no re-entrancy issues, because LINK.transfer does not yield
                // control flow.
                require(s_linkToken.transfer(payee, juelsAmount), "insufficient funds");
                s_rewardFromAggregatorRoundId[transmitter.index] = s_hotVars.latestAggregatorRoundId;
                s_transmitters[transmitterAddress].paymentJuels = 0;
                emit OraclePaid(transmitterAddress, payee, juelsAmount, s_linkToken);
              }
            }
            // _payOracles pays out all transmitters, and zeros out their balances.
            //
            // It's much more gas-efficient to do this as a single operation, to avoid
            // hitting storage too much.
            function _payOracles()
              internal
            {
              unchecked {
                LinkTokenInterface linkToken = s_linkToken;
                uint32 latestAggregatorRoundId = s_hotVars.latestAggregatorRoundId;
                uint32[maxNumOracles] memory rewardFromAggregatorRoundId = s_rewardFromAggregatorRoundId;
                address[] memory transmitters = s_transmittersList;
                for (uint transmitteridx = 0; transmitteridx < transmitters.length; transmitteridx++) {
                  uint256 reimbursementAmountJuels = s_transmitters[transmitters[transmitteridx]].paymentJuels;
                  s_transmitters[transmitters[transmitteridx]].paymentJuels = 0;
                  uint256 obsCount = latestAggregatorRoundId - rewardFromAggregatorRoundId[transmitteridx];
                  uint256 juelsAmount =
                    obsCount * uint256(s_hotVars.observationPaymentGjuels) * (1 gwei) + reimbursementAmountJuels;
                  if (juelsAmount > 0) {
                      address payee = s_payees[transmitters[transmitteridx]];
                      // Poses no re-entrancy issues, because LINK.transfer does not yield
                      // control flow.
                      require(linkToken.transfer(payee, juelsAmount), "insufficient funds");
                      rewardFromAggregatorRoundId[transmitteridx] = latestAggregatorRoundId;
                      emit OraclePaid(transmitters[transmitteridx], payee, juelsAmount, linkToken);
                    }
                }
                // "Zero" the accounting storage variables
                s_rewardFromAggregatorRoundId = rewardFromAggregatorRoundId;
              }
            }
            /**
             * @notice withdraw any available funds left in the contract, up to amount, after accounting for the funds due to participants in past reports
             * @param recipient address to send funds to
             * @param amount maximum amount to withdraw, denominated in LINK-wei.
             * @dev access control provided by billingAccessController
             */
            function withdrawFunds(
              address recipient,
              uint256 amount
            )
              external
            {
              require(msg.sender == owner() || s_billingAccessController.hasAccess(msg.sender, msg.data),
                "Only owner&billingAdmin can call");
              uint256 linkDue = _totalLinkDue();
              uint256 linkBalance = s_linkToken.balanceOf(address(this));
              require(linkBalance >= linkDue, "insufficient balance");
              require(s_linkToken.transfer(recipient, _min(linkBalance - linkDue, amount)), "insufficient funds");
            }
            // Total LINK due to participants in past reports (denominated in Juels).
            function _totalLinkDue()
              internal
              view
              returns (uint256 linkDue)
            {
              // Argument for overflow safety: We do all computations in
              // uint256s. The inputs to linkDue are:
              // - the <= 31 observation rewards each of which has less than
              //   64 bits (32 bits for observationPaymentGjuels, 32 bits
              //   for wei/gwei conversion). Hence 69 bits are sufficient for this part.
              // - the <= 31 gas reimbursements, each of which consists of at most 96
              //   bits. Hence 101 bits are sufficient for this part.
              // So we never need more than 102 bits.
              address[] memory transmitters = s_transmittersList;
              uint256 n = transmitters.length;
              uint32 latestAggregatorRoundId = s_hotVars.latestAggregatorRoundId;
              uint32[maxNumOracles] memory rewardFromAggregatorRoundId = s_rewardFromAggregatorRoundId;
              for (uint i = 0; i < n; i++) {
                linkDue += latestAggregatorRoundId - rewardFromAggregatorRoundId[i];
              }
              // Convert observationPaymentGjuels to uint256, or this overflows!
              linkDue *= uint256(s_hotVars.observationPaymentGjuels) * (1 gwei);
              for (uint i = 0; i < n; i++) {
                linkDue += uint256(s_transmitters[transmitters[i]].paymentJuels);
              }
            }
            /**
             * @notice allows oracles to check that sufficient LINK balance is available
             * @return availableBalance LINK available on this contract, after accounting for outstanding obligations. can become negative
             */
            function linkAvailableForPayment()
              external
              view
              returns (int256 availableBalance)
            {
              // there are at most one billion LINK, so this cast is safe
              int256 balance = int256(s_linkToken.balanceOf(address(this)));
              // according to the argument in the definition of _totalLinkDue,
              // _totalLinkDue is never greater than 2**102, so this cast is safe
              int256 due = int256(_totalLinkDue());
              // safe from overflow according to above sizes
              return int256(balance) - int256(due);
            }
            /**
             * @notice number of observations oracle is due to be reimbursed for
             * @param transmitterAddress address used by oracle for signing or transmitting reports
             */
            function oracleObservationCount(address transmitterAddress)
              external
              view
              returns (uint32)
            {
              Transmitter memory transmitter = s_transmitters[transmitterAddress];
              if (!transmitter.active) { return 0; }
              return s_hotVars.latestAggregatorRoundId - s_rewardFromAggregatorRoundId[transmitter.index];
            }
            /***************************************************************************
             * Section: Transmitter Payment
             **************************************************************************/
            // Gas price at which the transmitter should be reimbursed, in gwei/gas
            function _reimbursementGasPriceGwei(
              uint256 txGasPriceGwei,
              uint256 reasonableGasPriceGwei,
              uint256 maximumGasPriceGwei
            )
              internal
              pure
              returns (uint256)
            {
              // this happens on the path for transmissions. we'd rather pay out
              // a wrong reward than risk a liveness failure due to a revert.
              unchecked {
                // Reward the transmitter for choosing an efficient gas price: if they manage
                // to come in lower than considered reasonable, give them half the savings.
                uint256 gasPriceGwei = txGasPriceGwei;
                if (txGasPriceGwei < reasonableGasPriceGwei) {
                  // Give transmitter half the savings for coming in under the reasonable gas price
                  gasPriceGwei += (reasonableGasPriceGwei - txGasPriceGwei) / 2;
                }
                // Don't reimburse a gas price higher than maximumGasPriceGwei
                return _min(gasPriceGwei, maximumGasPriceGwei);
              }
            }
            // gas reimbursement due the transmitter, in wei
            function _transmitterGasCostWei(
              uint256 initialGas,
              uint256 gasPriceGwei,
              uint256 callDataGas,
              uint256 accountingGas,
              uint256 leftGas
            )
              internal
              pure
              returns (uint256)
            {
              // this happens on the path for transmissions. we'd rather pay out
              // a wrong reward than risk a liveness failure due to a revert.
              unchecked {
                require(initialGas >= leftGas, "leftGas cannot exceed initialGas");
                uint256 usedGas =
                  initialGas - leftGas + // observed gas usage
                  callDataGas + accountingGas; // estimated gas usage
                uint256 fullGasCostWei = usedGas * gasPriceGwei * (1 gwei);
                return fullGasCostWei;
              }
            }
            function _payTransmitter(
              HotVars memory hotVars,
              int192 juelsPerFeeCoin,
              uint32 initialGas,
              address transmitter
            )
              internal
              virtual
            {
              // this happens on the path for transmissions. we'd rather pay out
              // a wrong reward than risk a liveness failure due to a revert.
              unchecked {
                // we can't deal with negative juelsPerFeeCoin, better to just not pay
                if (juelsPerFeeCoin < 0) {
                  return;
                }
                // Reimburse transmitter of the report for gas usage
                uint256 gasPriceGwei = _reimbursementGasPriceGwei(
                  tx.gasprice / (1 gwei), // convert to ETH-gwei units
                  hotVars.reasonableGasPriceGwei,
                  hotVars.maximumGasPriceGwei
                );
                // The following is only an upper bound, as it ignores the cheaper cost for
                // 0 bytes. Safe from overflow, because calldata just isn't that long.
                uint256 callDataGasCost = 16 * msg.data.length;
                uint256 gasLeft = gasleft();
                uint256 gasCostEthWei = _transmitterGasCostWei(
                  uint256(initialGas),
                  gasPriceGwei,
                  callDataGasCost,
                  hotVars.accountingGas,
                  gasLeft
                );
                // Even if we assume absurdly large values, this still does not overflow. With
                // - usedGas <= 1'000'000 gas <= 2**20 gas
                // - weiPerGas <= 1'000'000 gwei <= 2**50 wei
                // - hence gasCostEthWei <= 2**70
                // - juelsPerFeeCoin <= 2**96 (more than the entire supply)
                // we still fit into 166 bits
                uint256 gasCostJuels = (gasCostEthWei * uint192(juelsPerFeeCoin))/1e18;
                uint96 oldTransmitterPaymentJuels = s_transmitters[transmitter].paymentJuels;
                uint96 newTransmitterPaymentJuels = uint96(uint256(oldTransmitterPaymentJuels) +
                  gasCostJuels + uint256(hotVars.transmissionPaymentGjuels) * (1 gwei));
                // overflow *should* never happen, but if it does, let's not persist it.
                if (newTransmitterPaymentJuels < oldTransmitterPaymentJuels) {
                  return;
                }
                s_transmitters[transmitter].paymentJuels = newTransmitterPaymentJuels;
              }
            }
            /***************************************************************************
             * Section: Payee Management
             **************************************************************************/
            // Addresses at which oracles want to receive payments, by transmitter address
            mapping (address /* transmitter */ => address /* payment address */)
              internal
              s_payees;
            // Payee addresses which must be approved by the owner
            mapping (address /* transmitter */ => address /* payment address */)
              internal
              s_proposedPayees;
            /**
             * @notice emitted when a transfer of an oracle's payee address has been initiated
             * @param transmitter address from which the oracle sends reports to the transmit method
             * @param current the payee address for the oracle, prior to this setting
             * @param proposed the proposed new payee address for the oracle
             */
            event PayeeshipTransferRequested(
              address indexed transmitter,
              address indexed current,
              address indexed proposed
            );
            /**
             * @notice emitted when a transfer of an oracle's payee address has been completed
             * @param transmitter address from which the oracle sends reports to the transmit method
             * @param current the payee address for the oracle, prior to this setting
             */
            event PayeeshipTransferred(
              address indexed transmitter,
              address indexed previous,
              address indexed current
            );
            /**
             * @notice sets the payees for transmitting addresses
             * @param transmitters addresses oracles use to transmit the reports
             * @param payees addresses of payees corresponding to list of transmitters
             * @dev must be called by owner
             * @dev cannot be used to change payee addresses, only to initially populate them
             */
            function setPayees(
              address[] calldata transmitters,
              address[] calldata payees
            )
              external
              onlyOwner()
            {
              require(transmitters.length == payees.length, "transmitters.size != payees.size");
              for (uint i = 0; i < transmitters.length; i++) {
                address transmitter = transmitters[i];
                address payee = payees[i];
                address currentPayee = s_payees[transmitter];
                bool zeroedOut = currentPayee == address(0);
                require(zeroedOut || currentPayee == payee, "payee already set");
                s_payees[transmitter] = payee;
                if (currentPayee != payee) {
                  emit PayeeshipTransferred(transmitter, currentPayee, payee);
                }
              }
            }
            /**
             * @notice first step of payeeship transfer (safe transfer pattern)
             * @param transmitter transmitter address of oracle whose payee is changing
             * @param proposed new payee address
             * @dev can only be called by payee address
             */
            function transferPayeeship(
              address transmitter,
              address proposed
            )
              external
            {
              require(msg.sender == s_payees[transmitter], "only current payee can update");
              require(msg.sender != proposed, "cannot transfer to self");
              address previousProposed = s_proposedPayees[transmitter];
              s_proposedPayees[transmitter] = proposed;
              if (previousProposed != proposed) {
                emit PayeeshipTransferRequested(transmitter, msg.sender, proposed);
              }
            }
            /**
             * @notice second step of payeeship transfer (safe transfer pattern)
             * @param transmitter transmitter address of oracle whose payee is changing
             * @dev can only be called by proposed new payee address
             */
            function acceptPayeeship(
              address transmitter
            )
              external
            {
              require(msg.sender == s_proposedPayees[transmitter], "only proposed payees can accept");
              address currentPayee = s_payees[transmitter];
              s_payees[transmitter] = msg.sender;
              s_proposedPayees[transmitter] = address(0);
              emit PayeeshipTransferred(transmitter, currentPayee, msg.sender);
            }
            /***************************************************************************
             * Section: TypeAndVersionInterface
             **************************************************************************/
            function typeAndVersion()
              external
              override
              pure
              virtual
              returns (string memory)
            {
              return "OCR2Aggregator 1.0.0";
            }
            /***************************************************************************
             * Section: Helper Functions
             **************************************************************************/
            function _min(
              uint256 a,
              uint256 b
            )
              internal
              pure
              returns (uint256)
            {
              unchecked {
                if (a < b) { return a; }
                return b;
              }
            }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity =0.8.19;
          import "./interfaces/TypeAndVersionInterface.sol";
          import "./lib/ConfigDigestUtilEVMSimple.sol";
          import "./OwnerIsCreator.sol";
          import "./OCR2Abstract.sol";
          /// @title OCRConfigurationStoreEVMSimple
          /// @notice This contract stores configurations for protocol versions OCR2 and
          /// above in contract storage. It uses the "EVMSimple" config digester.
          contract OCRConfigurationStoreEVMSimple is TypeAndVersionInterface {
              struct ConfigurationEVMSimple {
                  address[] signers;
                  address[] transmitters;
                  bytes onchainConfig;
                  bytes offchainConfig;
                  address contractAddress;
                  uint64 offchainConfigVersion;
                  uint32 configCount;
                  uint8 f;
              }
              /// @notice a list of configurations keyed by their digest
              mapping(bytes32 => ConfigurationEVMSimple) internal s_configurations;
              /// @notice emitted when a new configuration is added
              event NewConfiguration(bytes32 indexed configDigest);
              /// @notice adds a new configuration to the store
              function addConfig(ConfigurationEVMSimple calldata configuration) external returns (bytes32) {
                  bytes32 configDigest = ConfigDigestUtilEVMSimple.configDigestFromConfigData(
                      block.chainid,
                      configuration.contractAddress,
                      configuration.configCount,
                      configuration.signers,
                      configuration.transmitters,
                      configuration.f,
                      configuration.onchainConfig,
                      configuration.offchainConfigVersion,
                      configuration.offchainConfig
                  );
                  s_configurations[configDigest] = configuration;
                  emit NewConfiguration(configDigest);
                  return configDigest;
              }
              /// @notice reads a configuration from the store
              function readConfig(bytes32 configDigest) external view returns (ConfigurationEVMSimple memory) {
                  return s_configurations[configDigest];
              }
              /// @inheritdoc TypeAndVersionInterface
              function typeAndVersion() external override pure virtual returns (string memory)
              {
                  return "OCRConfigurationStoreEVMSimple 1.0.0";
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.0;
          import "./ConfirmedOwner.sol";
          /**
           * @title The OwnerIsCreator contract
           * @notice A contract with helpers for basic contract ownership.
           */
          contract OwnerIsCreator is ConfirmedOwner {
            constructor(
            )
              ConfirmedOwner(
                msg.sender
              )
            {
            }
          }// SPDX-License-Identifier: MIT
          pragma solidity ^0.8.0;
          import "./SimpleWriteAccessController.sol";
          /**
           * @title SimpleReadAccessController
           * @notice Gives access to:
           * - any externally owned account (note that offchain actors can always read
           * any contract storage regardless of onchain access control measures, so this
           * does not weaken the access control while improving usability)
           * - accounts explicitly added to an access list
           * @dev SimpleReadAccessController is not suitable for access controlling writes
           * since it grants any externally owned account access! See
           * SimpleWriteAccessController for that.
           */
          contract SimpleReadAccessController is SimpleWriteAccessController {
            /**
             * @notice Returns the access of an address
             * @param _user The address to query
             */
            function hasAccess(
              address _user,
              bytes memory _calldata
            )
              public
              view
              virtual
              override
              returns (bool)
            {
              return super.hasAccess(_user, _calldata) || _user == tx.origin;
            }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.0;
          import "./OwnerIsCreator.sol";
          import "./interfaces/AccessControllerInterface.sol";
          /**
           * @title SimpleWriteAccessController
           * @notice Gives access to accounts explicitly added to an access list by the
           * controller's owner.
           * @dev does not make any special permissions for externally, see
           * SimpleReadAccessController for that.
           */
          contract SimpleWriteAccessController is AccessControllerInterface, OwnerIsCreator {
            bool public checkEnabled;
            mapping(address => bool) internal accessList;
            event AddedAccess(address user);
            event RemovedAccess(address user);
            event CheckAccessEnabled();
            event CheckAccessDisabled();
            constructor()
            // TODO
            // this is modified from the version in the Chainlink monorepo
            //  OwnerIsCreator()
            {
              checkEnabled = true;
            }
            /**
             * @notice Returns the access of an address
             * @param _user The address to query
             */
            function hasAccess(
              address _user,
              bytes memory
            )
              public
              view
              virtual
              override
              returns (bool)
            {
              return accessList[_user] || !checkEnabled;
            }
            /**
             * @notice Adds an address to the access list
             * @param _user The address to add
             */
            function addAccess(address _user)
              external
              onlyOwner()
            {
              if (!accessList[_user]) {
                accessList[_user] = true;
                emit AddedAccess(_user);
              }
            }
            /**
             * @notice Removes an address from the access list
             * @param _user The address to remove
             */
            function removeAccess(address _user)
              external
              onlyOwner()
            {
              if (accessList[_user]) {
                accessList[_user] = false;
                emit RemovedAccess(_user);
              }
            }
            /**
             * @notice makes the access check enforced
             */
            function enableAccessCheck()
              external
              onlyOwner()
            {
              if (!checkEnabled) {
                checkEnabled = true;
                emit CheckAccessEnabled();
              }
            }
            /**
             * @notice makes the access check unenforced
             */
            function disableAccessCheck()
              external
              onlyOwner()
            {
              if (checkEnabled) {
                checkEnabled = false;
                emit CheckAccessDisabled();
              }
            }
            /**
             * @dev reverts if the caller does not have access
             */
            modifier checkAccess() {
              require(hasAccess(msg.sender, msg.data), "No access");
              _;
            }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.0;
          interface AccessControllerInterface {
            function hasAccess(address user, bytes calldata data) external view returns (bool);
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.0;
          interface AggregatorInterface {
            function latestAnswer() external view returns (int256);
            function latestTimestamp() external view returns (uint256);
            function latestRound() external view returns (uint256);
            function getAnswer(uint256 roundId) external view returns (int256);
            function getTimestamp(uint256 roundId) external view returns (uint256);
            event AnswerUpdated(int256 indexed current, uint256 indexed roundId, uint256 updatedAt);
            event NewRound(uint256 indexed roundId, address indexed startedBy, uint256 startedAt);
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.0;
          import "./AggregatorInterface.sol";
          import "./AggregatorV3Interface.sol";
          interface AggregatorV2V3Interface is AggregatorInterface, AggregatorV3Interface
          {
          }// SPDX-License-Identifier: MIT
          pragma solidity ^0.8.0;
          interface AggregatorV3Interface {
            function decimals() external view returns (uint8);
            function description() external view returns (string memory);
            function version() external view returns (uint256);
            function getRoundData(uint80 _roundId)
              external
              view
              returns (
                uint80 roundId,
                int256 answer,
                uint256 startedAt,
                uint256 updatedAt,
                uint80 answeredInRound
              );
            function latestRoundData()
              external
              view
              returns (
                uint80 roundId,
                int256 answer,
                uint256 startedAt,
                uint256 updatedAt,
                uint80 answeredInRound
              );
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.0;
          interface AggregatorValidatorInterface {
            function validate(
              uint256 previousRoundId,
              int256 previousAnswer,
              uint256 currentRoundId,
              int256 currentAnswer
            ) external returns (bool);
          }// SPDX-License-Identifier: MIT
          pragma solidity ^0.8.0;
          interface LinkTokenInterface {
            function allowance(address owner, address spender) external view returns (uint256 remaining);
            function approve(address spender, uint256 value) external returns (bool success);
            function balanceOf(address owner) external view returns (uint256 balance);
            function decimals() external view returns (uint8 decimalPlaces);
            function decreaseApproval(address spender, uint256 addedValue) external returns (bool success);
            function increaseApproval(address spender, uint256 subtractedValue) external;
            function name() external view returns (string memory tokenName);
            function symbol() external view returns (string memory tokenSymbol);
            function totalSupply() external view returns (uint256 totalTokensIssued);
            function transfer(address to, uint256 value) external returns (bool success);
            function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool success);
            function transferFrom(address from, address to, uint256 value) external returns (bool success);
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.0;
          interface OwnableInterface {
            function owner()
              external
              returns (
                address
              );
            function transferOwnership(
              address recipient
            )
              external;
            function acceptOwnership()
              external;
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.0;
          interface TypeAndVersionInterface{
            function typeAndVersion()
              external
              pure
              returns (string memory);
          }// SPDX-License-Identifier: MIT
          pragma solidity ^0.8.0;
          /// @title ConfigDigestUtilEVMSimple
          /// @notice ConfigDigest related utility functions for "EVMSimple" config
          /// digester
          library ConfigDigestUtilEVMSimple {
              function configDigestFromConfigData(
                  uint256 chainId,
                  address contractAddress,
                  uint64 configCount,
                  address[] memory signers,
                  address[] memory transmitters,
                  uint8 f,
                  bytes memory onchainConfig,
                  uint64 offchainConfigVersion,
                  bytes memory offchainConfig
              ) internal pure returns (bytes32)
              {
                  uint256 hash = uint256(
                      keccak256(
                          abi.encode(
                              chainId,
                              contractAddress,
                              configCount,
                              signers,
                              transmitters,
                              f,
                              onchainConfig,
                              offchainConfigVersion,
                              offchainConfig
                  )));
                  uint256 prefixMask = type(uint256).max << (256-16); // 0xFFFF00..00
                  uint256 prefix = 0x0001 << (256-16); // 0x000100..00
                  return bytes32((prefix & prefixMask) | (hash & ~prefixMask));
              }
          }