ETH Price: $1,972.82 (+0.21%)
Gas: 0.04 Gwei
 

Overview

ETH Balance

816.810797170871357305 ETH

Eth Value

$1,611,424.38 (@ $1,972.82/ETH)

More Info

Private Name Tags

Multichain Info

No addresses found
Age:1H
Reset Filter

Transaction Hash
Method
Block
From
To

There are no matching entries

Please try again later

Age:1H
Reset Filter

Advanced mode:
Parent Transaction Hash Method Block
From
To

There are no matching entries

Update your filters to view other transactions

View All Internal Transactions
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading

Similar Match Source Code
This contract matches the deployed Bytecode of the Source Code for Contract 0x440e7268...6Bac7B667
The constructor portion of the code might be different and could alter the actual behaviour of the contract

Contract Name:
PrivacyPoolSimple

Compiler Version
v0.8.28+commit.7893614a

Optimization Enabled:
Yes with 10000 runs

Other Settings:
cancun EvmVersion

Contract Source Code (Solidity Standard Json-Input format)

// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.28;

/*

Made with ♥ for 0xBow by

░██╗░░░░░░░██╗░█████╗░███╗░░██╗██████╗░███████╗██████╗░██╗░░░░░░█████╗░███╗░░██╗██████╗░
░██║░░██╗░░██║██╔══██╗████╗░██║██╔══██╗██╔════╝██╔══██╗██║░░░░░██╔══██╗████╗░██║██╔══██╗
░╚██╗████╗██╔╝██║░░██║██╔██╗██║██║░░██║█████╗░░██████╔╝██║░░░░░███████║██╔██╗██║██║░░██║
░░████╔═████║░██║░░██║██║╚████║██║░░██║██╔══╝░░██╔══██╗██║░░░░░██╔══██║██║╚████║██║░░██║
░░╚██╔╝░╚██╔╝░╚█████╔╝██║░╚███║██████╔╝███████╗██║░░██║███████╗██║░░██║██║░╚███║██████╔╝
░░░╚═╝░░░╚═╝░░░╚════╝░╚═╝░░╚══╝╚═════╝░╚══════╝╚═╝░░╚═╝╚══════╝╚═╝░░╚═╝╚═╝░░╚══╝╚═════╝░

https://defi.sucks/

*/

import {Constants} from 'contracts/lib/Constants.sol';

import {PrivacyPool} from 'contracts/PrivacyPool.sol';
import {IPrivacyPoolSimple} from 'interfaces/IPrivacyPool.sol';

/**
 * @title PrivacyPoolSimple
 * @notice Native asset implementation of Privacy Pool.
 */
contract PrivacyPoolSimple is PrivacyPool, IPrivacyPoolSimple {
  // @notice Initializes the state addresses
  constructor(
    address _entrypoint,
    address _withdrawalVerifier,
    address _ragequitVerifier
  ) PrivacyPool(_entrypoint, _withdrawalVerifier, _ragequitVerifier, Constants.NATIVE_ASSET) {}

  /**
   * @notice Handle receiving native asset asset
   * @param _amount The amount of asset receiving
   * @inheritdoc PrivacyPool
   */
  function _pull(address, uint256 _amount) internal override(PrivacyPool) {
    // Check the amount matches the value sent
    if (msg.value != _amount) revert InsufficientValue();
  }

  /**
   * @notice Handle sending native asset
   * @param _recipient The address of the user receiving the asset
   * @param _amount The amount of native asset being sent
   * @inheritdoc PrivacyPool
   */
  function _push(address _recipient, uint256 _amount) internal override(PrivacyPool) {
    /// Try to send native asset to recipient
    (bool _success,) = _recipient.call{value: _amount}('');
    if (!_success) revert FailedToSendNativeAsset();
  }
}

File 2 of 15 : Constants.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.28;

library Constants {
  uint256 constant SNARK_SCALAR_FIELD =
    21_888_242_871_839_275_222_246_405_745_257_275_088_548_364_400_416_034_343_698_204_186_575_808_495_617;

  address constant NATIVE_ASSET = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
}

// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.28;

/*

Made with ♥ for 0xBow by

░██╗░░░░░░░██╗░█████╗░███╗░░██╗██████╗░███████╗██████╗░██╗░░░░░░█████╗░███╗░░██╗██████╗░
░██║░░██╗░░██║██╔══██╗████╗░██║██╔══██╗██╔════╝██╔══██╗██║░░░░░██╔══██╗████╗░██║██╔══██╗
░╚██╗████╗██╔╝██║░░██║██╔██╗██║██║░░██║█████╗░░██████╔╝██║░░░░░███████║██╔██╗██║██║░░██║
░░████╔═████║░██║░░██║██║╚████║██║░░██║██╔══╝░░██╔══██╗██║░░░░░██╔══██║██║╚████║██║░░██║
░░╚██╔╝░╚██╔╝░╚█████╔╝██║░╚███║██████╔╝███████╗██║░░██║███████╗██║░░██║██║░╚███║██████╔╝
░░░╚═╝░░░╚═╝░░░╚════╝░╚═╝░░╚══╝╚═════╝░╚══════╝╚═╝░░╚═╝╚══════╝╚═╝░░╚═╝╚═╝░░╚══╝╚═════╝░

https://defi.sucks/

*/

import {PoseidonT4} from 'poseidon/PoseidonT4.sol';

import {Constants} from './lib/Constants.sol';
import {ProofLib} from './lib/ProofLib.sol';

import {IPrivacyPool} from 'interfaces/IPrivacyPool.sol';

import {State} from './State.sol';

/**
 * @title PrivacyPool
 * @notice Allows publicly depositing and privately withdrawing funds.
 * @dev Withdrawals require a valid proof of being approved by an ASP.
 * @dev Deposits can be irreversibly suspended by the Entrypoint, while withdrawals can't.
 */
abstract contract PrivacyPool is State, IPrivacyPool {
  using ProofLib for ProofLib.WithdrawProof;
  using ProofLib for ProofLib.RagequitProof;

  /**
   * @notice Does a series of sanity checks on the proof public signals
   * @param _withdrawal The withdrawal data structure containing withdrawal details
   * @param _proof The withdrawal proof data structure containing proof details
   */
  modifier validWithdrawal(Withdrawal memory _withdrawal, ProofLib.WithdrawProof memory _proof) {
    // Check caller is the allowed processooor
    if (msg.sender != _withdrawal.processooor) revert InvalidProcessooor();

    // Check the context matches to ensure its integrity
    if (_proof.context() != uint256(keccak256(abi.encode(_withdrawal, SCOPE))) % Constants.SNARK_SCALAR_FIELD) {
      revert ContextMismatch();
    }

    // Check the tree depth signals are less than the max tree depth
    if (_proof.stateTreeDepth() > MAX_TREE_DEPTH || _proof.ASPTreeDepth() > MAX_TREE_DEPTH) revert InvalidTreeDepth();

    // Check the state root is known
    if (!_isKnownRoot(_proof.stateRoot())) revert UnknownStateRoot();

    // Check the ASP root is the latest
    if (_proof.ASPRoot() != ENTRYPOINT.latestRoot()) revert IncorrectASPRoot();
    _;
  }

  /**
   * @notice Initializes the contract state addresses
   * @param _entrypoint Address of the Entrypoint that operates this pool
   * @param _withdrawalVerifier Address of the Groth16 verifier for withdrawal proofs
   * @param _ragequitVerifier Address of the Groth16 verifier for ragequit proofs
   * @param _asset Address of the pool asset
   */
  constructor(
    address _entrypoint,
    address _withdrawalVerifier,
    address _ragequitVerifier,
    address _asset
  ) State(_asset, _entrypoint, _withdrawalVerifier, _ragequitVerifier) {}

  /*///////////////////////////////////////////////////////////////
                             USER METHODS 
  //////////////////////////////////////////////////////////////*/

  /// @inheritdoc IPrivacyPool
  function deposit(
    address _depositor,
    uint256 _value,
    uint256 _precommitmentHash
  ) external payable onlyEntrypoint returns (uint256 _commitment) {
    // Check deposits are enabled
    if (dead) revert PoolIsDead();

    if (_value >= type(uint128).max) revert InvalidDepositValue();

    // Compute label
    uint256 _label = uint256(keccak256(abi.encodePacked(SCOPE, ++nonce))) % Constants.SNARK_SCALAR_FIELD;
    // Store depositor
    depositors[_label] = _depositor;

    // Compute commitment hash
    _commitment = PoseidonT4.hash([_value, _label, _precommitmentHash]);

    // Insert commitment in state (revert if already present)
    _insert(_commitment);

    // Pull funds from caller
    _pull(msg.sender, _value);

    emit Deposited(_depositor, _commitment, _label, _value, _precommitmentHash);
  }

  /// @inheritdoc IPrivacyPool
  function withdraw(
    Withdrawal memory _withdrawal,
    ProofLib.WithdrawProof memory _proof
  ) external validWithdrawal(_withdrawal, _proof) {
    // Verify proof with Groth16 verifier
    if (!WITHDRAWAL_VERIFIER.verifyProof(_proof.pA, _proof.pB, _proof.pC, _proof.pubSignals)) revert InvalidProof();

    // Mark existing commitment nullifier as spent
    _spend(_proof.existingNullifierHash());

    // Insert new commitment in state
    _insert(_proof.newCommitmentHash());

    // Transfer out funds to procesooor
    _push(_withdrawal.processooor, _proof.withdrawnValue());

    emit Withdrawn(
      _withdrawal.processooor, _proof.withdrawnValue(), _proof.existingNullifierHash(), _proof.newCommitmentHash()
    );
  }

  /// @inheritdoc IPrivacyPool
  function ragequit(ProofLib.RagequitProof memory _proof) external {
    // Check if caller is original depositor
    uint256 _label = _proof.label();
    if (depositors[_label] != msg.sender) revert OnlyOriginalDepositor();

    // Verify proof with Groth16 verifier
    if (!RAGEQUIT_VERIFIER.verifyProof(_proof.pA, _proof.pB, _proof.pC, _proof.pubSignals)) revert InvalidProof();

    // Check commitment exists in state
    if (!_isInState(_proof.commitmentHash())) revert InvalidCommitment();

    // Mark existing commitment nullifier as spent
    _spend(_proof.nullifierHash());

    // Transfer out funds to ragequitter
    _push(msg.sender, _proof.value());

    emit Ragequit(msg.sender, _proof.commitmentHash(), _proof.label(), _proof.value());
  }

  /*///////////////////////////////////////////////////////////////
                             WIND DOWN
  //////////////////////////////////////////////////////////////*/

  /// @inheritdoc IPrivacyPool
  function windDown() external onlyEntrypoint {
    // Check pool is still alive
    if (dead) revert PoolIsDead();

    // Die
    dead = true;

    emit PoolDied();
  }

  /*///////////////////////////////////////////////////////////////
                          ASSET OVERRIDES
  //////////////////////////////////////////////////////////////*/

  /**
   * @notice Handle receiving an asset
   * @dev To be implemented by an asset specific contract
   * @param _sender The address of the user sending funds
   * @param _value The amount of asset being received
   */
  function _pull(address _sender, uint256 _value) internal virtual;

  /**
   * @notice Handle sending an asset
   * @dev To be implemented by an asset specific contract
   * @param _recipient The address of the user receiving funds
   * @param _value The amount of asset being sent
   */
  function _push(address _recipient, uint256 _value) internal virtual;
}

// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.28;

import {ProofLib} from '../contracts/lib/ProofLib.sol';
import {IState} from 'interfaces/IState.sol';

/**
 * @title IPrivacyPool
 * @notice Interface for the PrivacyPool contract
 */
interface IPrivacyPool is IState {
  /*///////////////////////////////////////////////////////////////
                              STRUCTS
  //////////////////////////////////////////////////////////////*/

  /**
   * @notice Struct for the withdrawal request
   * @dev The integrity of this data is ensured by the `context` signal in the proof
   * @param processooor The allowed address to process the withdrawal
   * @param data Encoded arbitrary data used by the Entrypoint
   */
  struct Withdrawal {
    address processooor;
    bytes data;
  }

  /*///////////////////////////////////////////////////////////////
                              EVENTS
  //////////////////////////////////////////////////////////////*/

  /**
   * @notice Emitted when making a user deposit
   * @param _depositor The address of the depositor
   * @param _commitment The commitment hash
   * @param _label The deposit generated label
   * @param _value The deposited amount
   * @param _precommitmentHash The deposit precommitment hash
   */
  event Deposited(
    address indexed _depositor, uint256 _commitment, uint256 _label, uint256 _value, uint256 _precommitmentHash
  );

  /**
   * @notice Emitted when processing a withdrawal
   * @param _processooor The address which processed the withdrawal
   * @param _value The withdrawn amount
   * @param _spentNullifier The spent nullifier
   * @param _newCommitment The new commitment hash
   */
  event Withdrawn(address indexed _processooor, uint256 _value, uint256 _spentNullifier, uint256 _newCommitment);

  /**
   * @notice Emitted when ragequitting a commitment
   * @param _ragequitter The address who ragequit
   * @param _commitment The ragequit commitment
   * @param _label The commitment label
   * @param _value The ragequit amount
   */
  event Ragequit(address indexed _ragequitter, uint256 _commitment, uint256 _label, uint256 _value);

  /**
   * @notice Emitted irreversibly suspending deposits
   */
  event PoolDied();

  /*///////////////////////////////////////////////////////////////
                              ERRORS
  //////////////////////////////////////////////////////////////*/

  /**
   * @notice Thrown when failing to verify a withdrawal proof through the Groth16 verifier
   */
  error InvalidProof();

  /**
   * @notice Thrown when trying to spend a commitment that does not exist in the state
   */
  error InvalidCommitment();

  /**
   * @notice Thrown when calling `withdraw` while not being the allowed processooor
   */
  error InvalidProcessooor();

  /**
   * @notice Thrown when calling `withdraw` with a ASP or state tree depth greater or equal than the max tree depth
   */
  error InvalidTreeDepth();

  /**
   * @notice Thrown when trying to deposit an amount higher than 2**128
   */
  error InvalidDepositValue();

  /**
   * @notice Thrown when providing an invalid scope for this pool
   */
  error ScopeMismatch();

  /**
   * @notice Thrown when providing an invalid context for the pool and withdrawal
   */
  error ContextMismatch();

  /**
   * @notice Thrown when providing an unknown or outdated state root
   */
  error UnknownStateRoot();

  /**
   * @notice Thrown when providing an unknown or outdated ASP root
   */
  error IncorrectASPRoot();

  /**
   * @notice Thrown when trying to ragequit while not being the original depositor
   */
  error OnlyOriginalDepositor();

  /*///////////////////////////////////////////////////////////////
                              LOGIC
  //////////////////////////////////////////////////////////////*/

  /**
   * @notice Deposit funds into the Privacy Pool
   * @dev Only callable by the Entrypoint
   * @param _depositor The depositor address
   * @param _value The value being deposited
   * @param _precommitment The precommitment hash
   * @return _commitment The commitment hash
   */
  function deposit(
    address _depositor,
    uint256 _value,
    uint256 _precommitment
  ) external payable returns (uint256 _commitment);

  /**
   * @notice Privately withdraw funds by spending an existing commitment
   * @param _w The `Withdrawal` struct
   * @param _p The `WithdrawProof` struct
   */
  function withdraw(Withdrawal memory _w, ProofLib.WithdrawProof memory _p) external;

  /**
   * @notice Publicly withdraw funds to original depositor without exposing secrets
   * @dev Only callable by the original depositor
   * @param _p the `RagequitProof` struct
   */
  function ragequit(ProofLib.RagequitProof memory _p) external;

  /**
   * @notice Irreversibly suspends deposits
   * @dev Withdrawals can never be disabled
   * @dev Only callable by the Entrypoint
   */
  function windDown() external;
}

/**
 * @title IPrivacyPoolSimple
 * @notice Interface for the PrivacyPool native asset implementation
 */
interface IPrivacyPoolSimple is IPrivacyPool {
  /*///////////////////////////////////////////////////////////////
                              ERRORS
  //////////////////////////////////////////////////////////////*/

  /**
   * @notice Thrown when sending less amount of native asset than required
   */
  error InsufficientValue();

  /**
   * @notice Thrown when failing to send native asset to an account
   */
  error FailedToSendNativeAsset();
}

/**
 * @title IPrivacyPoolComplex
 * @notice Interface for the PrivacyPool ERC20 implementation
 */
interface IPrivacyPoolComplex is IPrivacyPool {
  /*///////////////////////////////////////////////////////////////
                              ERRORS
  //////////////////////////////////////////////////////////////*/

  /**
   * @notice Thrown when sending sending any amount of native asset
   */
  error NativeAssetNotAccepted();

  /**
   * @notice Thrown when trying to set up a complex pool with the native asset
   */
  error NativeAssetNotSupported();
}

/// SPDX-License-Identifier: MIT
pragma solidity >=0.7.0;

library PoseidonT4 {
  uint constant F = 21888242871839275222246405745257275088548364400416034343698204186575808495617;

  uint constant M00 = 0x236d13393ef85cc48a351dd786dd7a1de5e39942296127fd87947223ae5108ad;
  uint constant M01 = 0x2a75a171563b807db525be259699ab28fe9bc7fb1f70943ff049bc970e841a0c;
  uint constant M02 = 0x2070679e798782ef592a52ca9cef820d497ad2eecbaa7e42f366b3e521c4ed42;
  uint constant M03 = 0x2f545e578202c9732488540e41f783b68ff0613fd79375f8ba8b3d30958e7677;
  uint constant M10 = 0x277686494f7644bbc4a9b194e10724eb967f1dc58718e59e3cedc821b2a7ae19;
  uint constant M11 = 0x083abff5e10051f078e2827d092e1ae808b4dd3e15ccc3706f38ce4157b6770e;
  uint constant M12 = 0x2e18c8570d20bf5df800739a53da75d906ece318cd224ab6b3a2be979e2d7eab;
  uint constant M13 = 0x23810bf82877fc19bff7eefeae3faf4bb8104c32ba4cd701596a15623d01476e;
  uint constant M20 = 0x023db68784e3f0cc0b85618826a9b3505129c16479973b0a84a4529e66b09c62;
  uint constant M21 = 0x1a5ad71bbbecd8a97dc49cfdbae303ad24d5c4741eab8b7568a9ff8253a1eb6f;
  uint constant M22 = 0x0fa86f0f27e4d3dd7f3367ce86f684f1f2e4386d3e5b9f38fa283c6aa723b608;
  uint constant M23 = 0x014fcd5eb0be6d5beeafc4944034cf321c068ef930f10be2207ed58d2a34cdd6;
  uint constant M30 = 0x1d359d245f286c12d50d663bae733f978af08cdbd63017c57b3a75646ff382c1;
  uint constant M31 = 0x0d745fd00dd167fb86772133640f02ce945004a7bc2c59e8790f725c5d84f0af;
  uint constant M32 = 0x03f3e6fab791f16628168e4b14dbaeb657035ee3da6b2ca83f0c2491e0b403eb;
  uint constant M33 = 0x00c15fc3a1d5733dd835eae0823e377f8ba4a8b627627cc2bb661c25d20fb52a;

  // See here for a simplified implementation: https://github.com/vimwitch/poseidon-solidity/blob/e57becdabb65d99fdc586fe1e1e09e7108202d53/contracts/Poseidon.sol#L40
  // Inspired by: https://github.com/iden3/circomlibjs/blob/v0.0.8/src/poseidon_slow.js
  function hash(uint[3] memory) public pure returns (uint) {
    assembly {
      // memory 0x00 to 0x3f (64 bytes) is scratch space for hash algos
      // we can use it in inline assembly because we're not calling e.g. keccak
      //
      // memory 0x80 is the default offset for free memory
      // we take inputs as a memory argument so we simply write over
      // that memory after loading it

      // we have the following variables at memory offsets
      // state0 - 0x00
      // state1 - 0x20
      // state2 - 0x80
      // state3 - 0xa0
      // state4 - ...

      function pRound(c0, c1, c2, c3) {
        let state0 := add(mload(0x0), c0)
        let state1 := add(mload(0x20), c1)
        let state2 := add(mload(0x80), c2)
        let state3 := add(mload(0xa0), c3)

        let p := mulmod(state0, state0, F)
        state0 := mulmod(mulmod(p, p, F), state0, F)

        mstore(0x0, mod(add(add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)), mulmod(state3, M30, F)), F))
        mstore(0x20, mod(add(add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)), mulmod(state3, M31, F)), F))
        mstore(0x80, mod(add(add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)), mulmod(state3, M32, F)), F))
        mstore(0xa0, mod(add(add(add(mulmod(state0, M03, F), mulmod(state1, M13, F)), mulmod(state2, M23, F)), mulmod(state3, M33, F)), F))
      }

      function fRound(c0, c1, c2, c3) {
        let state0 := add(mload(0x0), c0)
        let state1 := add(mload(0x20), c1)
        let state2 := add(mload(0x80), c2)
        let state3 := add(mload(0xa0), c3)

        let p := mulmod(state0, state0, F)
        state0 := mulmod(mulmod(p, p, F), state0, F)
        p := mulmod(state1, state1, F)
        state1 := mulmod(mulmod(p, p, F), state1, F)
        p := mulmod(state2, state2, F)
        state2 := mulmod(mulmod(p, p, F), state2, F)
        p := mulmod(state3, state3, F)
        state3 := mulmod(mulmod(p, p, F), state3, F)

        mstore(0x0, mod(add(add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)), mulmod(state3, M30, F)), F))
        mstore(0x20, mod(add(add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)), mulmod(state3, M31, F)), F))
        mstore(0x80, mod(add(add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)), mulmod(state3, M32, F)), F))
        mstore(0xa0, mod(add(add(add(mulmod(state0, M03, F), mulmod(state1, M13, F)), mulmod(state2, M23, F)), mulmod(state3, M33, F)), F))
      }

      // scratch variable for exponentiation
      let p

      {
        // load the inputs from memory
        let state1 := add(mod(mload(0x80), F), 0x265ddfe127dd51bd7239347b758f0a1320eb2cc7450acc1dad47f80c8dcf34d6)
        let state2 := add(mod(mload(0xa0), F), 0x199750ec472f1809e0f66a545e1e51624108ac845015c2aa3dfc36bab497d8aa)
        let state3 := add(mod(mload(0xc0), F), 0x157ff3fe65ac7208110f06a5f74302b14d743ea25067f0ffd032f787c7f1cdf8)

        p := mulmod(state1, state1, F)
        state1 := mulmod(mulmod(p, p, F), state1, F)
        p := mulmod(state2, state2, F)
        state2 := mulmod(mulmod(p, p, F), state2, F)
        p := mulmod(state3, state3, F)
        state3 := mulmod(mulmod(p, p, F), state3, F)

        // state0 pow5mod and M[] multiplications are pre-calculated

        mstore(
          0x0,
          mod(add(add(add(0x211184aac7468125da9b5527788aed6331caa8335774fe66f16acc6c66c456d7, mulmod(state1, M10, F)), mulmod(state2, M20, F)), mulmod(state3, M30, F)), F)
        )
        mstore(
          0x20,
          mod(add(add(add(0x19764435729b98150ca53b559b7b1bdd91692d645e831f4a30d30d510792687a, mulmod(state1, M11, F)), mulmod(state2, M21, F)), mulmod(state3, M31, F)), F)
        )
        mstore(
          0x80,
          mod(add(add(add(0x21f642c132b82c867835f1753eecedd4679085e8c78f6a0ae4a8cd81e9834bdf, mulmod(state1, M12, F)), mulmod(state2, M22, F)), mulmod(state3, M32, F)), F)
        )
        mstore(
          0xa0,
          mod(add(add(add(0x26bc2b5c607af61196105d955bd3d9b2cf795edcf9e39d1e508c542ca85d6be3, mulmod(state1, M13, F)), mulmod(state2, M23, F)), mulmod(state3, M33, F)), F)
        )
      }

      fRound(
        0x2e49c43c4569dd9c5fd35ac45fca33f10b15c590692f8beefe18f4896ac94902,
        0x0e35fb89981890520d4aef2b6d6506c3cb2f0b6973c24fa82731345ffa2d1f1e,
        0x251ad47cb15c4f1105f109ae5e944f1ba9d9e7806d667ffec6fe723002e0b996,
        0x13da07dc64d428369873e97160234641f8beb56fdd05e5f3563fa39d9c22df4e
      )

      fRound(
        0x0c009b84e650e6d23dc00c7dccef7483a553939689d350cd46e7b89055fd4738,
        0x011f16b1c63a854f01992e3956f42d8b04eb650c6d535eb0203dec74befdca06,
        0x0ed69e5e383a688f209d9a561daa79612f3f78d0467ad45485df07093f367549,
        0x04dba94a7b0ce9e221acad41472b6bbe3aec507f5eb3d33f463672264c9f789b
      )

      fRound(
        0x0a3f2637d840f3a16eb094271c9d237b6036757d4bb50bf7ce732ff1d4fa28e8,
        0x259a666f129eea198f8a1c502fdb38fa39b1f075569564b6e54a485d1182323f,
        0x28bf7459c9b2f4c6d8e7d06a4ee3a47f7745d4271038e5157a32fdf7ede0d6a1,
        0x0a1ca941f057037526ea200f489be8d4c37c85bbcce6a2aeec91bd6941432447
      )

      pRound(
        0x0c6f8f958be0e93053d7fd4fc54512855535ed1539f051dcb43a26fd926361cf,
        0x123106a93cd17578d426e8128ac9d90aa9e8a00708e296e084dd57e69caaf811,
        0x26e1ba52ad9285d97dd3ab52f8e840085e8fa83ff1e8f1877b074867cd2dee75,
        0x1cb55cad7bd133de18a64c5c47b9c97cbe4d8b7bf9e095864471537e6a4ae2c5
      )

      pRound(
        0x1dcd73e46acd8f8e0e2c7ce04bde7f6d2a53043d5060a41c7143f08e6e9055d0,
        0x011003e32f6d9c66f5852f05474a4def0cda294a0eb4e9b9b12b9bb4512e5574,
        0x2b1e809ac1d10ab29ad5f20d03a57dfebadfe5903f58bafed7c508dd2287ae8c,
        0x2539de1785b735999fb4dac35ee17ed0ef995d05ab2fc5faeaa69ae87bcec0a5
      )

      pRound(
        0x0c246c5a2ef8ee0126497f222b3e0a0ef4e1c3d41c86d46e43982cb11d77951d,
        0x192089c4974f68e95408148f7c0632edbb09e6a6ad1a1c2f3f0305f5d03b527b,
        0x1eae0ad8ab68b2f06a0ee36eeb0d0c058529097d91096b756d8fdc2fb5a60d85,
        0x179190e5d0e22179e46f8282872abc88db6e2fdc0dee99e69768bd98c5d06bfb
      )

      pRound(
        0x29bb9e2c9076732576e9a81c7ac4b83214528f7db00f31bf6cafe794a9b3cd1c,
        0x225d394e42207599403efd0c2464a90d52652645882aac35b10e590e6e691e08,
        0x064760623c25c8cf753d238055b444532be13557451c087de09efd454b23fd59,
        0x10ba3a0e01df92e87f301c4b716d8a394d67f4bf42a75c10922910a78f6b5b87
      )

      pRound(
        0x0e070bf53f8451b24f9c6e96b0c2a801cb511bc0c242eb9d361b77693f21471c,
        0x1b94cd61b051b04dd39755ff93821a73ccd6cb11d2491d8aa7f921014de252fb,
        0x1d7cb39bafb8c744e148787a2e70230f9d4e917d5713bb050487b5aa7d74070b,
        0x2ec93189bd1ab4f69117d0fe980c80ff8785c2961829f701bb74ac1f303b17db
      )

      pRound(
        0x2db366bfdd36d277a692bb825b86275beac404a19ae07a9082ea46bd83517926,
        0x062100eb485db06269655cf186a68532985275428450359adc99cec6960711b8,
        0x0761d33c66614aaa570e7f1e8244ca1120243f92fa59e4f900c567bf41f5a59b,
        0x20fc411a114d13992c2705aa034e3f315d78608a0f7de4ccf7a72e494855ad0d
      )

      pRound(
        0x25b5c004a4bdfcb5add9ec4e9ab219ba102c67e8b3effb5fc3a30f317250bc5a,
        0x23b1822d278ed632a494e58f6df6f5ed038b186d8474155ad87e7dff62b37f4b,
        0x22734b4c5c3f9493606c4ba9012499bf0f14d13bfcfcccaa16102a29cc2f69e0,
        0x26c0c8fe09eb30b7e27a74dc33492347e5bdff409aa3610254413d3fad795ce5
      )

      pRound(
        0x070dd0ccb6bd7bbae88eac03fa1fbb26196be3083a809829bbd626df348ccad9,
        0x12b6595bdb329b6fb043ba78bb28c3bec2c0a6de46d8c5ad6067c4ebfd4250da,
        0x248d97d7f76283d63bec30e7a5876c11c06fca9b275c671c5e33d95bb7e8d729,
        0x1a306d439d463b0816fc6fd64cc939318b45eb759ddde4aa106d15d9bd9baaaa
      )

      pRound(
        0x28a8f8372e3c38daced7c00421cb4621f4f1b54ddc27821b0d62d3d6ec7c56cf,
        0x0094975717f9a8a8bb35152f24d43294071ce320c829f388bc852183e1e2ce7e,
        0x04d5ee4c3aa78f7d80fde60d716480d3593f74d4f653ae83f4103246db2e8d65,
        0x2a6cf5e9aa03d4336349ad6fb8ed2269c7bef54b8822cc76d08495c12efde187
      )

      pRound(
        0x2304d31eaab960ba9274da43e19ddeb7f792180808fd6e43baae48d7efcba3f3,
        0x03fd9ac865a4b2a6d5e7009785817249bff08a7e0726fcb4e1c11d39d199f0b0,
        0x00b7258ded52bbda2248404d55ee5044798afc3a209193073f7954d4d63b0b64,
        0x159f81ada0771799ec38fca2d4bf65ebb13d3a74f3298db36272c5ca65e92d9a
      )

      pRound(
        0x1ef90e67437fbc8550237a75bc28e3bb9000130ea25f0c5471e144cf4264431f,
        0x1e65f838515e5ff0196b49aa41a2d2568df739bc176b08ec95a79ed82932e30d,
        0x2b1b045def3a166cec6ce768d079ba74b18c844e570e1f826575c1068c94c33f,
        0x0832e5753ceb0ff6402543b1109229c165dc2d73bef715e3f1c6e07c168bb173
      )

      pRound(
        0x02f614e9cedfb3dc6b762ae0a37d41bab1b841c2e8b6451bc5a8e3c390b6ad16,
        0x0e2427d38bd46a60dd640b8e362cad967370ebb777bedff40f6a0be27e7ed705,
        0x0493630b7c670b6deb7c84d414e7ce79049f0ec098c3c7c50768bbe29214a53a,
        0x22ead100e8e482674decdab17066c5a26bb1515355d5461a3dc06cc85327cea9
      )

      pRound(
        0x25b3e56e655b42cdaae2626ed2554d48583f1ae35626d04de5084e0b6d2a6f16,
        0x1e32752ada8836ef5837a6cde8ff13dbb599c336349e4c584b4fdc0a0cf6f9d0,
        0x2fa2a871c15a387cc50f68f6f3c3455b23c00995f05078f672a9864074d412e5,
        0x2f569b8a9a4424c9278e1db7311e889f54ccbf10661bab7fcd18e7c7a7d83505
      )

      pRound(
        0x044cb455110a8fdd531ade530234c518a7df93f7332ffd2144165374b246b43d,
        0x227808de93906d5d420246157f2e42b191fe8c90adfe118178ddc723a5319025,
        0x02fcca2934e046bc623adead873579865d03781ae090ad4a8579d2e7a6800355,
        0x0ef915f0ac120b876abccceb344a1d36bad3f3c5ab91a8ddcbec2e060d8befac
      )

      pRound(
        0x1797130f4b7a3e1777eb757bc6f287f6ab0fb85f6be63b09f3b16ef2b1405d38,
        0x0a76225dc04170ae3306c85abab59e608c7f497c20156d4d36c668555decc6e5,
        0x1fffb9ec1992d66ba1e77a7b93209af6f8fa76d48acb664796174b5326a31a5c,
        0x25721c4fc15a3f2853b57c338fa538d85f8fbba6c6b9c6090611889b797b9c5f
      )

      pRound(
        0x0c817fd42d5f7a41215e3d07ba197216adb4c3790705da95eb63b982bfcaf75a,
        0x13abe3f5239915d39f7e13c2c24970b6df8cf86ce00a22002bc15866e52b5a96,
        0x2106feea546224ea12ef7f39987a46c85c1bc3dc29bdbd7a92cd60acb4d391ce,
        0x21ca859468a746b6aaa79474a37dab49f1ca5a28c748bc7157e1b3345bb0f959
      )

      pRound(
        0x05ccd6255c1e6f0c5cf1f0df934194c62911d14d0321662a8f1a48999e34185b,
        0x0f0e34a64b70a626e464d846674c4c8816c4fb267fe44fe6ea28678cb09490a4,
        0x0558531a4e25470c6157794ca36d0e9647dbfcfe350d64838f5b1a8a2de0d4bf,
        0x09d3dca9173ed2faceea125157683d18924cadad3f655a60b72f5864961f1455
      )

      pRound(
        0x0328cbd54e8c0913493f866ed03d218bf23f92d68aaec48617d4c722e5bd4335,
        0x2bf07216e2aff0a223a487b1a7094e07e79e7bcc9798c648ee3347dd5329d34b,
        0x1daf345a58006b736499c583cb76c316d6f78ed6a6dffc82111e11a63fe412df,
        0x176563472456aaa746b694c60e1823611ef39039b2edc7ff391e6f2293d2c404
      )

      pRound(
        0x2ef1e0fad9f08e87a3bb5e47d7e33538ca964d2b7d1083d4fb0225035bd3f8db,
        0x226c9b1af95babcf17b2b1f57c7310179c1803dec5ae8f0a1779ed36c817ae2a,
        0x14bce3549cc3db7428126b4c3a15ae0ff8148c89f13fb35d35734eb5d4ad0def,
        0x2debff156e276bb5742c3373f2635b48b8e923d301f372f8e550cfd4034212c7
      )

      pRound(
        0x2d4083cf5a87f5b6fc2395b22e356b6441afe1b6b29c47add7d0432d1d4760c7,
        0x0c225b7bcd04bf9c34b911262fdc9c1b91bf79a10c0184d89c317c53d7161c29,
        0x03152169d4f3d06ec33a79bfac91a02c99aa0200db66d5aa7b835265f9c9c8f3,
        0x0b61811a9210be78b05974587486d58bddc8f51bfdfebbb87afe8b7aa7d3199c
      )

      pRound(
        0x203e000cad298daaf7eba6a5c5921878b8ae48acf7048f16046d637a533b6f78,
        0x1a44bf0937c722d1376672b69f6c9655ba7ee386fda1112c0757143d1bfa9146,
        0x0376b4fae08cb03d3500afec1a1f56acb8e0fde75a2106d7002f59c5611d4daa,
        0x00780af2ca1cad6465a2171250fdfc32d6fc241d3214177f3d553ef363182185
      )

      pRound(
        0x10774d9ab80c25bdeb808bedfd72a8d9b75dbe18d5221c87e9d857079bdc31d5,
        0x10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e8,
        0x00544b8338791518b2c7645a50392798b21f75bb60e3596170067d00141cac16,
        0x222c01175718386f2e2e82eb122789e352e105a3b8fa852613bc534433ee428c
      )

      pRound(
        0x2840d045e9bc22b259cfb8811b1e0f45b77f7bdb7f7e2b46151a1430f608e3c5,
        0x062752f86eebe11a009c937e468c335b04554574c2990196508e01fa5860186b,
        0x06041bdac48205ac87adb87c20a478a71c9950c12a80bc0a55a8e83eaaf04746,
        0x04a533f236c422d1ff900a368949b0022c7a2ae092f308d82b1dcbbf51f5000d
      )

      pRound(
        0x13e31d7a67232fd811d6a955b3d4f25dfe066d1e7dc33df04bde50a2b2d05b2a,
        0x011c2683ae91eb4dfbc13d6357e8599a9279d1648ff2c95d2f79905bb13920f1,
        0x0b0d219346b8574525b1a270e0b4cba5d56c928e3e2c2bd0a1ecaed015aaf6ae,
        0x14abdec8db9c6dc970291ee638690209b65080781ef9fd13d84c7a726b5f1364
      )

      pRound(
        0x1a0b70b4b26fdc28fcd32aa3d266478801eb12202ef47ced988d0376610be106,
        0x278543721f96d1307b6943f9804e7fe56401deb2ef99c4d12704882e7278b607,
        0x16eb59494a9776cf57866214dbd1473f3f0738a325638d8ba36535e011d58259,
        0x2567a658a81ffb444f240088fa5524c69a9e53eeab6b7f8c41c3479dcf8c644a
      )

      pRound(
        0x29aa1d7c151e9ad0a7ab39f1abd9cf77ab78e0215a5715a6b882ade840bb13d8,
        0x15c091233e60efe0d4bbfce2b36415006a4f017f9a85388ce206b91f99f2c984,
        0x16bd7d22ff858e5e0882c2c999558d77e7673ad5f1915f9feb679a8115f014cf,
        0x02db50480a07be0eb2c2e13ed6ef4074c0182d9b668b8e08ffe6769250042025
      )

      pRound(
        0x05e4a220e6a3bc9f7b6806ec9d6cdba186330ef2bf7adb4c13ba866343b73119,
        0x1dda05ebc30170bc98cbf2a5ee3b50e8b5f70bc424d39fa4104d37f1cbcf7a42,
        0x0184bef721888187f645b6fee3667f3c91da214414d89ba5cd301f22b0de8990,
        0x1498a307e68900065f5e8276f62aef1c37414b84494e1577ad1a6d64341b78ec
      )

      pRound(
        0x25f40f82b31dacc4f4939800b9d2c3eacef737b8fab1f864fe33548ad46bd49d,
        0x09d317cc670251943f6f5862a30d2ea9e83056ce4907bfbbcb1ff31ce5bb9650,
        0x2f77d77786d979b23ba4ce4a4c1b3bd0a41132cd467a86ab29b913b6cf3149d0,
        0x0f53dafd535a9f4473dc266b6fccc6841bbd336963f254c152f89e785f729bbf
      )

      pRound(
        0x25c1fd72e223045265c3a099e17526fa0e6976e1c00baf16de96de85deef2fa2,
        0x2a902c8980c17faae368d385d52d16be41af95c84eaea3cf893e65d6ce4a8f62,
        0x1ce1580a3452ecf302878c8976b82be96676dd114d1dc8d25527405762f83529,
        0x24a6073f91addc33a49a1fa306df008801c5ec569609034d2fc50f7f0f4d0056
      )

      pRound(
        0x25e52dbd6124530d9fc27fe306d71d4583e07ca554b5d1577f256c68b0be2b74,
        0x23dffae3c423fa7a93468dbccfb029855974be4d0a7b29946796e5b6cd70f15d,
        0x06342da370cc0d8c49b77594f6b027c480615d50be36243a99591bc9924ed6f5,
        0x2754114281286546b75f09f115fc751b4778303d0405c1b4cc7df0d8e9f63925
      )

      pRound(
        0x15c19e8534c5c1a8862c2bc1d119eddeabf214153833d7bdb59ee197f8187cf5,
        0x265fe062766d08fab4c78d0d9ef3cabe366f3be0a821061679b4b3d2d77d5f3e,
        0x13ccf689d67a3ec9f22cb7cd0ac3a327d377ac5cd0146f048debfd098d3ec7be,
        0x17662f7456789739f81cd3974827a887d92a5e05bdf3fe6b9fbccca4524aaebd
      )

      pRound(
        0x21b29c76329b31c8ef18631e515f7f2f82ca6a5cca70cee4e809fd624be7ad5d,
        0x18137478382aadba441eb97fe27901989c06738165215319939eb17b01fa975c,
        0x2bc07ea2bfad68e8dc724f5fef2b37c2d34f761935ffd3b739ceec4668f37e88,
        0x2ddb2e376f54d64a563840480df993feb4173203c2bd94ad0e602077aef9a03e
      )

      pRound(
        0x277eb50f2baa706106b41cb24c602609e8a20f8d72f613708adb25373596c3f7,
        0x0d4de47e1aba34269d0c620904f01a56b33fc4b450c0db50bb7f87734c9a1fe5,
        0x0b8442bfe9e4a1b4428673b6bd3eea6f9f445697058f134aae908d0279a29f0c,
        0x11fe5b18fbbea1a86e06930cb89f7d4a26e186a65945e96574247fddb720f8f5
      )

      pRound(
        0x224026f6dfaf71e24d25d8f6d9f90021df5b774dcad4d883170e4ad89c33a0d6,
        0x0b2ca6a999fe6887e0704dad58d03465a96bc9e37d1091f61bc9f9c62bbeb824,
        0x221b63d66f0b45f9d40c54053a28a06b1d0a4ce41d364797a1a7e0c96529f421,
        0x30185c48b7b2f1d53d4120801b047d087493bce64d4d24aedce2f4836bb84ad4
      )

      pRound(
        0x23f5d372a3f0e3cba989e223056227d3533356f0faa48f27f8267318632a61f0,
        0x2716683b32c755fd1bf8235ea162b1f388e1e0090d06162e8e6dfbe4328f3e3b,
        0x0977545836866fa204ca1d853ec0909e3d140770c80ac67dc930c69748d5d4bc,
        0x1444e8f592bdbfd8025d91ab4982dd425f51682d31472b05e81c43c0f9434b31
      )

      pRound(
        0x26e04b65e9ca8270beb74a1c5cb8fee8be3ffbfe583f7012a00f874e7718fbe3,
        0x22a5c2fa860d11fe34ee47a5cd9f869800f48f4febe29ad6df69816fb1a914d2,
        0x174b54d9907d8f5c6afd672a738f42737ec338f3a0964c629f7474dd44c5c8d7,
        0x1db1db8aa45283f31168fa66694cf2808d2189b87c8c8143d56c871907b39b87
      )

      pRound(
        0x1530bf0f46527e889030b8c7b7dfde126f65faf8cce0ab66387341d813d1bfd1,
        0x0b73f613993229f59f01c1cec8760e9936ead9edc8f2814889330a2f2bade457,
        0x29c25a22fe2164604552aaea377f448d587ab977fc8227787bd2dc0f36bcf41e,
        0x2b30d53ed1759bfb8503da66c92cf4077abe82795dc272b377df57d77c875526
      )

      pRound(
        0x12f6d703b5702aab7b7b7e69359d53a2756c08c85ede7227cf5f0a2916787cd2,
        0x2520e18300afda3f61a40a0b8837293a55ad01071028d4841ffa9ac706364113,
        0x1ec9daea860971ecdda8ed4f346fa967ac9bc59278277393c68f09fa03b8b95f,
        0x0a99b3e178db2e2e432f5cd5bef8fe4483bf5cbf70ed407c08aae24b830ad725
      )

      pRound(
        0x07cda9e63db6e39f086b89b601c2bbe407ee0abac3c817a1317abad7c5778492,
        0x08c9c65a4f955e8952d571b191bb0adb49bd8290963203b35d48aab38f8fc3a3,
        0x2737f8ce1d5a67b349590ddbfbd709ed9af54a2a3f2719d33801c9c17bdd9c9e,
        0x1049a6c65ff019f0d28770072798e8b7909432bd0c129813a9f179ba627f7d6a
      )

      pRound(
        0x18b4fe968732c462c0ea5a9beb27cecbde8868944fdf64ee60a5122361daeddb,
        0x2ff2b6fd22df49d2440b2eaeeefa8c02a6f478cfcf11f1b2a4f7473483885d19,
        0x2ec5f2f1928fe932e56c789b8f6bbcb3e8be4057cbd8dbd18a1b352f5cef42ff,
        0x265a5eccd8b92975e33ad9f75bf3426d424a4c6a7794ee3f08c1d100378e545e
      )

      pRound(
        0x2405eaa4c0bde1129d6242bb5ada0e68778e656cfcb366bf20517da1dfd4279c,
        0x094c97d8c194c42e88018004cbbf2bc5fdb51955d8b2d66b76dd98a2dbf60417,
        0x2c30d5f33bb32c5c22b9979a605bf64d508b705221e6a686330c9625c2afe0b8,
        0x01a75666f6241f6825d01cc6dcb1622d4886ea583e87299e6aa2fc716fdb6cf5
      )

      pRound(
        0x0a3290e8398113ea4d12ac091e87be7c6d359ab9a66979fcf47bf2e87d382fcb,
        0x154ade9ca36e268dfeb38461425bb0d8c31219d8fa0dfc75ecd21bf69aa0cc74,
        0x27aa8d3e25380c0b1b172d79c6f22eee99231ef5dc69d8dc13a4b5095d028772,
        0x2cf4051e6cab48301a8b2e3bca6099d756bbdf485afa1f549d395bbcbd806461
      )

      pRound(
        0x301e70f729f3c94b1d3f517ddff9f2015131feab8afa5eebb0843d7f84b23e71,
        0x298beb64f812d25d8b4d9620347ab02332dc4cef113ae60d17a8d7a4c91f83bc,
        0x1b362e72a5f847f84d03fd291c3c471ed1c14a15b221680acf11a3f02e46aa95,
        0x0dc8a2146110c0b375432902999223d5aa1ef6e78e1e5ebcbc1d9ba41dc1c737
      )

      pRound(
        0x0a48663b34ce5e1c05dc93092cb69778cb21729a72ddc03a08afa1eb922ff279,
        0x0a87391fb1cd8cdf6096b64a82f9e95f0fe46f143b702d74545bb314881098ee,
        0x1b5b2946f7c28975f0512ff8e6ca362f8826edd7ea9c29f382ba8a2a0892fd5d,
        0x01001cf512ac241d47ebe2239219bc6a173a8bbcb8a5b987b4eac1f533315b6b
      )

      pRound(
        0x2fd977c70f645db4f704fa7d7693da727ac093d3fb5f5febc72beb17d8358a32,
        0x23c0039a3fab4ad3c2d7cc688164f39e761d5355c05444d99be763a97793a9c4,
        0x19d43ee0c6081c052c9c0df6161eaac1aec356cf435888e79f27f22ff03fa25d,
        0x2d9b10c2f2e7ac1afddccffd94a563028bf29b646d020830919f9d5ca1cefe59
      )

      pRound(
        0x2457ca6c2f2aa30ec47e4aff5a66f5ce2799283e166fc81cdae2f2b9f83e4267,
        0x0abc392fe85eda855820592445094022811ee8676ed6f0c3044dfb54a7c10b35,
        0x19d2cc5ca549d1d40cebcd37f3ea54f31161ac3993acf3101d2c2bc30eac1eb0,
        0x0f97ae3033ffa01608aafb26ae13cd393ee0e4ec041ba644a3d3ab546e98c9c8
      )

      pRound(
        0x16dbc78fd28b7fb8260e404cf1d427a7fa15537ea4e168e88a166496e88cfeca,
        0x240faf28f11499b916f085f73bc4f22eef8344e576f8ad3d1827820366d5e07b,
        0x0a1bb075aa37ff0cfe6c8531e55e1770eaba808c8fdb6dbf46f8cab58d9ef1af,
        0x2e47e15ea4a47ff1a6a853aaf3a644ca38d5b085ac1042fdc4a705a7ce089f4d
      )

      pRound(
        0x166e5bf073378348860ca4a9c09d39e1673ab059935f4df35fb14528375772b6,
        0x18b42d7ffdd2ea4faf235902f057a2740cacccd027233001ed10f96538f0916f,
        0x089cb1b032238f5e4914788e3e3c7ead4fc368020b3ed38221deab1051c37702,
        0x242acd3eb3a2f72baf7c7076dd165adf89f9339c7b971921d9e70863451dd8d1
      )

      pRound(
        0x174fbb104a4ee302bf47f2bd82fce896eac9a068283f326474af860457245c3b,
        0x17340e71d96f466d61f3058ce092c67d2891fb2bb318613f780c275fe1116c6b,
        0x1e8e40ac853b7d42f00f2e383982d024f098b9f8fd455953a2fd380c4df7f6b2,
        0x0529898dc0649907e1d4d5e284b8d1075198c55cad66e8a9bf40f92938e2e961
      )

      pRound(
        0x2162754db0baa030bf7de5bb797364dce8c77aa017ee1d7bf65f21c4d4e5df8f,
        0x12c7553698c4bf6f3ceb250ae00c58c2a9f9291efbde4c8421bef44741752ec6,
        0x292643e3ba2026affcb8c5279313bd51a733c93353e9d9c79cb723136526508e,
        0x00ccf13e0cb6f9d81d52951bea990bd5b6c07c5d98e66ff71db6e74d5b87d158
      )

      pRound(
        0x185d1e20e23b0917dd654128cf2f3aaab6723873cb30fc22b0f86c15ab645b4b,
        0x14c61c836d55d3df742bdf11c60efa186778e3de0f024c0f13fe53f8d8764e1f,
        0x0f356841b3f556fce5dbe4680457691c2919e2af53008184d03ee1195d72449e,
        0x1b8fd9ff39714e075df124f887bf40b383143374fd2080ba0c0a6b6e8fa5b3e8
      )

      pRound(
        0x0e86a8c2009c140ca3f873924e2aaa14fc3c8ae04e9df0b3e9103418796f6024,
        0x2e6c5e898f5547770e5462ad932fcdd2373fc43820ca2b16b0861421e79155c8,
        0x05d797f1ab3647237c14f9d1df032bc9ff9fe1a0ecd377972ce5fd5a0c014604,
        0x29a3110463a5aae76c3d152875981d0c1daf2dcd65519ef5ca8929851da8c008
      )

      pRound(
        0x2974da7bc074322273c3a4b91c05354cdc71640a8bbd1f864b732f8163883314,
        0x1ed0fb06699ba249b2a30621c05eb12ca29cb91aa082c8bfcce9c522889b47dc,
        0x1c793ef0dcc51123654ff26d8d863feeae29e8c572eca912d80c8ae36e40fe9b,
        0x1e6aac1c6d3dd3157956257d3d234ef18c91e82589a78169fbb4a8770977dc2f
      )

      pRound(
        0x1a20ada7576234eee6273dd6fa98b25ed037748080a47d948fcda33256fb6bf5,
        0x191033d6d85ceaa6fc7a9a23a6fd9996642d772045ece51335d49306728af96c,
        0x006e5979da7e7ef53a825aa6fddc3abfc76f200b3740b8b232ef481f5d06297b,
        0x0b0d7e69c651910bbef3e68d417e9fa0fbd57f596c8f29831eff8c0174cdb06d
      )

      pRound(
        0x25caf5b0c1b93bc516435ec084e2ecd44ac46dbbb033c5112c4b20a25c9cdf9d,
        0x12c1ea892cc31e0d9af8b796d9645872f7f77442d62fd4c8085b2f150f72472a,
        0x16af29695157aba9b8bbe3afeb245feee5a929d9f928b9b81de6dadc78c32aae,
        0x0136df457c80588dd687fb2f3be18691705b87ec5a4cfdc168d31084256b67dc
      )

      pRound(
        0x1639a28c5b4c81166aea984fba6e71479e07b1efbc74434db95a285060e7b089,
        0x03d62fbf82fd1d4313f8e650f587ec06816c28b700bdc50f7e232bd9b5ca9b76,
        0x11aeeb527dc8ce44b4d14aaddca3cfe2f77a1e40fc6da97c249830de1edfde54,
        0x13f9b9a41274129479c5e6138c6c8ee36a670e6bc68c7a49642b645807bfc824
      )

      fRound(
        0x0e4772fa3d75179dc8484cd26c7c1f635ddeeed7a939440c506cae8b7ebcd15b,
        0x1b39a00cbc81e427de4bdec58febe8d8b5971752067a612b39fc46a68c5d4db4,
        0x2bedb66e1ad5a1d571e16e2953f48731f66463c2eb54a245444d1c0a3a25707e,
        0x2cf0a09a55ca93af8abd068f06a7287fb08b193b608582a27379ce35da915dec
      )

      fRound(
        0x2d1bd78fa90e77aa88830cabfef2f8d27d1a512050ba7db0753c8fb863efb387,
        0x065610c6f4f92491f423d3071eb83539f7c0d49c1387062e630d7fd283dc3394,
        0x2d933ff19217a5545013b12873452bebcc5f9969033f15ec642fb464bd607368,
        0x1aa9d3fe4c644910f76b92b3e13b30d500dae5354e79508c3c49c8aa99e0258b
      )

      fRound(
        0x027ef04869e482b1c748638c59111c6b27095fa773e1aca078cea1f1c8450bdd,
        0x2b7d524c5172cbbb15db4e00668a8c449f67a2605d9ec03802e3fa136ad0b8fb,
        0x0c7c382443c6aa787c8718d86747c7f74693ae25b1e55df13f7c3c1dd735db0f,
        0x00b4567186bc3f7c62a7b56acf4f76207a1f43c2d30d0fe4a627dcdd9bd79078
      )

      {
        let state0 := add(mload(0x0), 0x1e41fc29b825454fe6d61737fe08b47fb07fe739e4c1e61d0337490883db4fd5)
        let state1 := add(mload(0x20), 0x12507cd556b7bbcc72ee6dafc616584421e1af872d8c0e89002ae8d3ba0653b6)
        let state2 := add(mload(0x80), 0x13d437083553006bcef312e5e6f52a5d97eb36617ef36fe4d77d3e97f71cb5db)
        let state3 := add(mload(0xa0), 0x163ec73251f85443687222487dda9a65467d90b22f0b38664686077c6a4486d5)

        p := mulmod(state0, state0, F)
        state0 := mulmod(mulmod(p, p, F), state0, F)
        p := mulmod(state1, state1, F)
        state1 := mulmod(mulmod(p, p, F), state1, F)
        p := mulmod(state2, state2, F)
        state2 := mulmod(mulmod(p, p, F), state2, F)
        p := mulmod(state3, state3, F)
        state3 := mulmod(mulmod(p, p, F), state3, F)

        mstore(0x0, mod(mod(add(add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)), mulmod(state3, M30, F)), F), F))
        return(0, 0x20)
      }
    }
  }
}

// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.28;

/**
 * @title ProofLib
 * @notice Facilitates accessing the public signals of a Groth16 proof.
 * @custom:semver 0.1.0
 */
library ProofLib {
  /*///////////////////////////////////////////////////////////////
                         WITHDRAWAL PROOF 
  //////////////////////////////////////////////////////////////*/

  /**
   * @notice Struct containing Groth16 proof elements and public signals for withdrawal verification
   * @dev The public signals array must match the order of public inputs/outputs in the circuit
   * @param pA First elliptic curve point (π_A) of the Groth16 proof, encoded as two field elements
   * @param pB Second elliptic curve point (π_B) of the Groth16 proof, encoded as 2x2 matrix of field elements
   * @param pC Third elliptic curve point (π_C) of the Groth16 proof, encoded as two field elements
   * @param pubSignals Array of public inputs and outputs:
   *        - [0] newCommitmentHash: Hash of the new commitment being created
   *        - [1] existingNullifierHash: Hash of the nullifier being spent
   *        - [2] withdrawnValue: Amount being withdrawn
   *        - [3] stateRoot: Current state root of the privacy pool
   *        - [4] stateTreeDepth: Current depth of the state tree
   *        - [5] ASPRoot: Current root of the Association Set Provider tree
   *        - [6] ASPTreeDepth: Current depth of the ASP tree
   *        - [7] context: Context value for the withdrawal operation
   */
  struct WithdrawProof {
    uint256[2] pA;
    uint256[2][2] pB;
    uint256[2] pC;
    uint256[8] pubSignals;
  }

  /**
   * @notice Retrieves the new commitment hash from the proof's public signals
   * @param _p The proof containing the public signals
   * @return The hash of the new commitment being created
   */
  function newCommitmentHash(WithdrawProof memory _p) internal pure returns (uint256) {
    return _p.pubSignals[0];
  }

  /**
   * @notice Retrieves the existing nullifier hash from the proof's public signals
   * @param _p The proof containing the public signals
   * @return The hash of the nullifier being spent in this withdrawal
   */
  function existingNullifierHash(WithdrawProof memory _p) internal pure returns (uint256) {
    return _p.pubSignals[1];
  }

  /**
   * @notice Retrieves the withdrawn value from the proof's public signals
   * @param _p The proof containing the public signals
   * @return The amount being withdrawn from Privacy Pool
   */
  function withdrawnValue(WithdrawProof memory _p) internal pure returns (uint256) {
    return _p.pubSignals[2];
  }

  /**
   * @notice Retrieves the state root from the proof's public signals
   * @param _p The proof containing the public signals
   * @return The root of the state tree at time of proof generation
   */
  function stateRoot(WithdrawProof memory _p) internal pure returns (uint256) {
    return _p.pubSignals[3];
  }

  /**
   * @notice Retrieves the state tree depth from the proof's public signals
   * @param _p The proof containing the public signals
   * @return The depth of the state tree at time of proof generation
   */
  function stateTreeDepth(WithdrawProof memory _p) internal pure returns (uint256) {
    return _p.pubSignals[4];
  }

  /**
   * @notice Retrieves the ASP root from the proof's public signals
   * @param _p The proof containing the public signals
   * @return The latest root of the ASP tree at time of proof generation
   */
  function ASPRoot(WithdrawProof memory _p) internal pure returns (uint256) {
    return _p.pubSignals[5];
  }

  /**
   * @notice Retrieves the ASP tree depth from the proof's public signals
   * @param _p The proof containing the public signals
   * @return The depth of the ASP tree at time of proof generation
   */
  function ASPTreeDepth(WithdrawProof memory _p) internal pure returns (uint256) {
    return _p.pubSignals[6];
  }

  /**
   * @notice Retrieves the context value from the proof's public signals
   * @param _p The proof containing the public signals
   * @return The context value binding the proof to specific withdrawal data
   */
  function context(WithdrawProof memory _p) internal pure returns (uint256) {
    return _p.pubSignals[7];
  }

  /*///////////////////////////////////////////////////////////////
                          RAGEQUIT PROOF 
  //////////////////////////////////////////////////////////////*/

  /**
   * @notice Struct containing Groth16 proof elements and public signals for ragequit verification
   * @dev The public signals array must match the order of public inputs/outputs in the circuit
   * @param pA First elliptic curve point (π_A) of the Groth16 proof, encoded as two field elements
   * @param pB Second elliptic curve point (π_B) of the Groth16 proof, encoded as 2x2 matrix of field elements
   * @param pC Third elliptic curve point (π_C) of the Groth16 proof, encoded as two field elements
   * @param pubSignals Array of public inputs and outputs:
   *        - [0] commitmentHash: Hash of the commitment being ragequit
   *        - [1] nullifierHash: Nullifier hash of commitment being ragequit
   *        - [2] value: Value of the commitment being ragequit
   *        - [3] label: Label of commitment
   */
  struct RagequitProof {
    uint256[2] pA;
    uint256[2][2] pB;
    uint256[2] pC;
    uint256[4] pubSignals;
  }

  /**
   * @notice Retrieves the new commitment hash from the proof's public signals
   * @param _p The ragequit proof containing the public signals
   * @return The new commitment hash
   */
  function commitmentHash(RagequitProof memory _p) internal pure returns (uint256) {
    return _p.pubSignals[0];
  }

  /**
   * @notice Retrieves the nullifier hash from the proof's public signals
   * @param _p The ragequit proof containing the public signals
   * @return The nullifier hash
   */
  function nullifierHash(RagequitProof memory _p) internal pure returns (uint256) {
    return _p.pubSignals[1];
  }

  /**
   * @notice Retrieves the commitment value from the proof's public signals
   * @param _p The ragequit proof containing the public signals
   * @return The commitment value
   */
  function value(RagequitProof memory _p) internal pure returns (uint256) {
    return _p.pubSignals[2];
  }

  /**
   * @notice Retrieves the commitment label from the proof's public signals
   * @param _p The ragequit proof containing the public signals
   * @return The commitment label
   */
  function label(RagequitProof memory _p) internal pure returns (uint256) {
    return _p.pubSignals[3];
  }
}

// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.28;

/*

Made with ♥ for 0xBow by

░██╗░░░░░░░██╗░█████╗░███╗░░██╗██████╗░███████╗██████╗░██╗░░░░░░█████╗░███╗░░██╗██████╗░
░██║░░██╗░░██║██╔══██╗████╗░██║██╔══██╗██╔════╝██╔══██╗██║░░░░░██╔══██╗████╗░██║██╔══██╗
░╚██╗████╗██╔╝██║░░██║██╔██╗██║██║░░██║█████╗░░██████╔╝██║░░░░░███████║██╔██╗██║██║░░██║
░░████╔═████║░██║░░██║██║╚████║██║░░██║██╔══╝░░██╔══██╗██║░░░░░██╔══██║██║╚████║██║░░██║
░░╚██╔╝░╚██╔╝░╚█████╔╝██║░╚███║██████╔╝███████╗██║░░██║███████╗██║░░██║██║░╚███║██████╔╝
░░░╚═╝░░░╚═╝░░░╚════╝░╚═╝░░╚══╝╚═════╝░╚══════╝╚═╝░░╚═╝╚══════╝╚═╝░░╚═╝╚═╝░░╚══╝╚═════╝░

https://defi.sucks

*/

import {Constants} from './lib/Constants.sol';
import {InternalLeanIMT, LeanIMTData} from 'lean-imt/InternalLeanIMT.sol';

import {IEntrypoint} from 'interfaces/IEntrypoint.sol';
import {IState} from 'interfaces/IState.sol';
import {IVerifier} from 'interfaces/IVerifier.sol';

/**
 * @title State
 * @notice Base contract for the managing the state of a Privacy Pool
 * @custom:semver 0.1.0
 */
abstract contract State is IState {
  using InternalLeanIMT for LeanIMTData;

  /// @inheritdoc IState
  uint32 public constant ROOT_HISTORY_SIZE = 64;
  /// @inheritdoc IState
  uint32 public constant MAX_TREE_DEPTH = 32;

  /// @inheritdoc IState
  address public immutable ASSET;
  /// @inheritdoc IState
  uint256 public immutable SCOPE;
  /// @inheritdoc IState
  IEntrypoint public immutable ENTRYPOINT;
  /// @inheritdoc IState
  IVerifier public immutable WITHDRAWAL_VERIFIER;
  /// @inheritdoc IState
  IVerifier public immutable RAGEQUIT_VERIFIER;

  /// @inheritdoc IState
  uint256 public nonce;
  /// @inheritdoc IState
  bool public dead;

  /// @inheritdoc IState
  mapping(uint256 _index => uint256 _root) public roots;
  /// @inheritdoc IState
  uint32 public currentRootIndex;

  // @notice The state merkle tree containing all commitments
  LeanIMTData internal _merkleTree;

  /// @inheritdoc IState
  mapping(uint256 _nullifierHash => bool _spent) public nullifierHashes;
  /// @inheritdoc IState
  mapping(uint256 _label => address _depositooor) public depositors;

  /**
   * @notice Check the caller is the Entrypoint
   */
  modifier onlyEntrypoint() {
    if (msg.sender != address(ENTRYPOINT)) revert OnlyEntrypoint();
    _;
  }

  /**
   * @notice Initialize the state addresses
   */
  constructor(address _asset, address _entrypoint, address _withdrawalVerifier, address _ragequitVerifier) {
    // Sanitize initial addresses
    if (_asset == address(0)) revert ZeroAddress();
    if (_entrypoint == address(0)) revert ZeroAddress();
    if (_ragequitVerifier == address(0)) revert ZeroAddress();
    if (_withdrawalVerifier == address(0)) revert ZeroAddress();

    // Store asset address
    ASSET = _asset;
    // Compute SCOPE
    SCOPE = uint256(keccak256(abi.encodePacked(address(this), block.chainid, _asset))) % Constants.SNARK_SCALAR_FIELD;

    ENTRYPOINT = IEntrypoint(_entrypoint);
    WITHDRAWAL_VERIFIER = IVerifier(_withdrawalVerifier);
    RAGEQUIT_VERIFIER = IVerifier(_ragequitVerifier);
  }

  /*///////////////////////////////////////////////////////////////
                              VIEWS
  //////////////////////////////////////////////////////////////*/

  /// @inheritdoc IState
  function currentRoot() external view returns (uint256 _root) {
    _root = _merkleTree._root();
  }

  /// @inheritdoc IState
  function currentTreeDepth() external view returns (uint256 _depth) {
    _depth = _merkleTree.depth;
  }

  /// @inheritdoc IState
  function currentTreeSize() external view returns (uint256 _size) {
    _size = _merkleTree.size;
  }

  /*///////////////////////////////////////////////////////////////
                        INTERNAL METHODS
  //////////////////////////////////////////////////////////////*/

  /**
   * @notice Spends a nullifier hash
   * @param _nullifierHash The nullifier hash to spend
   */
  function _spend(uint256 _nullifierHash) internal {
    // Check if the nullifier is already spent
    if (nullifierHashes[_nullifierHash]) revert NullifierAlreadySpent();

    // Mark as spent
    nullifierHashes[_nullifierHash] = true;
  }

  /**
   * @notice Insert a leaf into the state
   * @param _leaf The leaf to insert
   * @return _updatedRoot The new root after inserting the leaf
   */
  function _insert(uint256 _leaf) internal returns (uint256 _updatedRoot) {
    // Insert leaf in the tree
    _updatedRoot = _merkleTree._insert(_leaf);

    if (_merkleTree.depth > MAX_TREE_DEPTH) revert MaxTreeDepthReached();

    // Calculate the next index
    uint32 nextIndex = (currentRootIndex + 1) % ROOT_HISTORY_SIZE;

    // Store the root at the next index
    roots[nextIndex] = _updatedRoot;

    // Update currentRootIndex to point to the latest root
    currentRootIndex = nextIndex;

    emit LeafInserted(_merkleTree.size, _leaf, _updatedRoot);
  }

  /**
   * @notice Returns whether the root is a known root
   * @dev A circular buffer is used for root storage to decrease the cost of storing new roots
   * @dev Optimized to start search from most recent roots, improving average case performance
   * @param _root The root to check
   * @return Returns true if the root exists in the history, false otherwise
   */
  function _isKnownRoot(uint256 _root) internal view returns (bool) {
    if (_root == 0) return false;

    // Start from the most recent root (current index)
    uint32 _index = currentRootIndex;

    // Check all possible roots in the history
    for (uint32 _i = 0; _i < ROOT_HISTORY_SIZE; _i++) {
      if (_root == roots[_index]) return true;
      _index = (_index + ROOT_HISTORY_SIZE - 1) % ROOT_HISTORY_SIZE;
    }
    return false;
  }

  /**
   * @notice Returns whether a leaf is in the state
   * @param _leaf The leaf to check
   * @return Returns true if the leaf exists in the tree, false otherwise
   */
  function _isInState(uint256 _leaf) internal view returns (bool) {
    return _merkleTree._has(_leaf);
  }
}

// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.28;

import {IEntrypoint} from 'interfaces/IEntrypoint.sol';

import {IVerifier} from 'interfaces/IVerifier.sol';

/**
 * @title IState
 * @notice Interface for the State contract
 */
interface IState {
  /*///////////////////////////////////////////////////////////////
                              EVENTS
  //////////////////////////////////////////////////////////////*/

  /**
   * @notice Emitted when inserting a leaf into the Merkle Tree
   * @param _index The index of the leaf in the tree
   * @param _leaf The leaf value
   * @param _root The updated root
   */
  event LeafInserted(uint256 _index, uint256 _leaf, uint256 _root);

  /*///////////////////////////////////////////////////////////////
                              ERRORS
  //////////////////////////////////////////////////////////////*/

  /**
   * @notice Thrown when trying to call a method only available to the Entrypoint
   */
  error OnlyEntrypoint();

  /**
   * @notice Thrown when trying to deposit into a dead pool
   */
  error PoolIsDead();

  /**
   * @notice Thrown when trying to spend a nullifier that has already been spent
   */
  error NullifierAlreadySpent();

  /**
   * @notice Thrown when trying to initiate the ragequitting process of a commitment before the waiting period
   */
  error NotYetRagequitteable();

  /**
   * @notice Thrown when the max tree depth is reached and no more commitments can be inserted
   */
  error MaxTreeDepthReached();

  /**
   * @notice Thrown when trying to set a state variable as address zero
   */
  error ZeroAddress();

  /*///////////////////////////////////////////////////////////////
                              VIEWS 
  //////////////////////////////////////////////////////////////*/

  /**
   * @notice Returns the pool unique identifier
   * @return _scope The scope id
   */
  function SCOPE() external view returns (uint256 _scope);

  /**
   * @notice Returns the pool asset
   * @return _asset The asset address
   */
  function ASSET() external view returns (address _asset);

  /**
   * @notice Returns the root history size for root caching
   * @return _size The amount of valid roots to store
   */
  function ROOT_HISTORY_SIZE() external view returns (uint32 _size);

  /**
   * @notice Returns the maximum depth of the state tree
   * @dev Merkle tree depth must be capped at a fixed maximum because zero-knowledge circuits
   * compile to R1CS (Rank-1 Constraint System) constraints that must be determined at compile time.
   * R1CS cannot handle dynamic loops or recursion - all computation paths must be fully "unrolled"
   * into a fixed number of constraints. Since each level of the Merkle tree requires its own set
   * of constraints for hashing and path verification, we need to set a maximum depth that determines
   * the total constraint size of the circuit.
   * @return _maxDepth The max depth
   */
  function MAX_TREE_DEPTH() external view returns (uint32 _maxDepth);

  /**
   * @notice Returns the configured Entrypoint contract
   * @return _entrypoint The Entrypoint contract
   */
  function ENTRYPOINT() external view returns (IEntrypoint _entrypoint);

  /**
   * @notice Returns the configured Verifier contract for withdrawals
   * @return _verifier The Verifier contract
   */
  function WITHDRAWAL_VERIFIER() external view returns (IVerifier _verifier);

  /**
   * @notice Returns the configured Verifier contract for ragequits
   * @return _verifier The Verifier contract
   */
  function RAGEQUIT_VERIFIER() external view returns (IVerifier _verifier);

  /**
   * @notice Returns the current root index
   * @return _index The current index
   */
  function currentRootIndex() external view returns (uint32 _index);

  /**
   * @notice Returns the current state root
   * @return _root The current state root
   */
  function currentRoot() external view returns (uint256 _root);

  /**
   * @notice Returns the current state tree depth
   * @return _depth The current state tree depth
   */
  function currentTreeDepth() external view returns (uint256 _depth);

  /**
   * @notice Returns the current state tree size
   * @return _size The current state tree size
   */
  function currentTreeSize() external view returns (uint256 _size);

  /**
   * @notice Returns the current label nonce
   * @return _nonce The current nonce
   */
  function nonce() external view returns (uint256 _nonce);

  /**
   * @notice Returns the boolean indicating if the pool is dead
   * @return _dead The dead boolean
   */
  function dead() external view returns (bool _dead);

  /**
   * @notice Returns the root stored at an index
   * @param _index The root index
   * @return _root The root value
   */
  function roots(uint256 _index) external view returns (uint256 _root);

  /**
   * @notice Returns the spending status of a nullifier hash
   * @param _nullifierHash The nullifier hash
   * @return _spent The boolean indicating if it is spent
   */
  function nullifierHashes(uint256 _nullifierHash) external view returns (bool _spent);

  /**
   * @notice Returns the original depositor that generated a label
   * @param _label The label
   * @return _depositor The original depositor
   */
  function depositors(uint256 _label) external view returns (address _depositor);
}

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

import {PoseidonT3} from "poseidon-solidity/PoseidonT3.sol";
import {SNARK_SCALAR_FIELD} from "./Constants.sol";

struct LeanIMTData {
    // Tracks the current number of leaves in the tree.
    uint256 size;
    // Represents the current depth of the tree, which can increase as new leaves are inserted.
    uint256 depth;
    // A mapping from each level of the tree to the node value of the last even position at that level.
    // Used for efficient inserts, updates and root calculations.
    mapping(uint256 => uint256) sideNodes;
    // A mapping from leaf values to their respective indices in the tree.
    // This facilitates checks for leaf existence and retrieval of leaf positions.
    mapping(uint256 => uint256) leaves;
}

error WrongSiblingNodes();
error LeafGreaterThanSnarkScalarField();
error LeafCannotBeZero();
error LeafAlreadyExists();
error LeafDoesNotExist();

/// @title Lean Incremental binary Merkle tree.
/// @dev The LeanIMT is an optimized version of the BinaryIMT.
/// This implementation eliminates the use of zeroes, and make the tree depth dynamic.
/// When a node doesn't have the right child, instead of using a zero hash as in the BinaryIMT,
/// the node's value becomes that of its left child. Furthermore, rather than utilizing a static tree depth,
/// it is updated based on the number of leaves in the tree. This approach
/// results in the calculation of significantly fewer hashes, making the tree more efficient.
library InternalLeanIMT {
    /// @dev Inserts a new leaf into the incremental merkle tree.
    /// The function ensures that the leaf is valid according to the
    /// constraints of the tree and then updates the tree's structure accordingly.
    /// @param self: A storage reference to the 'LeanIMTData' struct.
    /// @param leaf: The value of the new leaf to be inserted into the tree.
    /// @return The new hash of the node after the leaf has been inserted.
    function _insert(LeanIMTData storage self, uint256 leaf) internal returns (uint256) {
        if (leaf >= SNARK_SCALAR_FIELD) {
            revert LeafGreaterThanSnarkScalarField();
        } else if (leaf == 0) {
            revert LeafCannotBeZero();
        } else if (_has(self, leaf)) {
            revert LeafAlreadyExists();
        }

        uint256 index = self.size;

        // Cache tree depth to optimize gas
        uint256 treeDepth = self.depth;

        // A new insertion can increase a tree's depth by at most 1,
        // and only if the number of leaves supported by the current
        // depth is less than the number of leaves to be supported after insertion.
        if (2 ** treeDepth < index + 1) {
            ++treeDepth;
        }

        self.depth = treeDepth;

        uint256 node = leaf;

        for (uint256 level = 0; level < treeDepth; ) {
            if ((index >> level) & 1 == 1) {
                node = PoseidonT3.hash([self.sideNodes[level], node]);
            } else {
                self.sideNodes[level] = node;
            }

            unchecked {
                ++level;
            }
        }

        self.size = ++index;

        self.sideNodes[treeDepth] = node;
        self.leaves[leaf] = index;

        return node;
    }

    /// @dev Inserts many leaves into the incremental merkle tree.
    /// The function ensures that the leaves are valid according to the
    /// constraints of the tree and then updates the tree's structure accordingly.
    /// @param self: A storage reference to the 'LeanIMTData' struct.
    /// @param leaves: The values of the new leaves to be inserted into the tree.
    /// @return The root after the leaves have been inserted.
    function _insertMany(LeanIMTData storage self, uint256[] calldata leaves) internal returns (uint256) {
        // Cache tree size to optimize gas
        uint256 treeSize = self.size;

        // Check that all the new values are correct to be added.
        for (uint256 i = 0; i < leaves.length; ) {
            if (leaves[i] >= SNARK_SCALAR_FIELD) {
                revert LeafGreaterThanSnarkScalarField();
            } else if (leaves[i] == 0) {
                revert LeafCannotBeZero();
            } else if (_has(self, leaves[i])) {
                revert LeafAlreadyExists();
            }

            self.leaves[leaves[i]] = treeSize + 1 + i;

            unchecked {
                ++i;
            }
        }

        // Array to save the nodes that will be used to create the next level of the tree.
        uint256[] memory currentLevelNewNodes;

        currentLevelNewNodes = leaves;

        // Cache tree depth to optimize gas
        uint256 treeDepth = self.depth;

        // Calculate the depth of the tree after adding the new values.
        // Unlike the 'insert' function, we need a while here as
        // N insertions can increase the tree's depth more than once.
        while (2 ** treeDepth < treeSize + leaves.length) {
            ++treeDepth;
        }

        self.depth = treeDepth;

        // First index to change in every level.
        uint256 currentLevelStartIndex = treeSize;

        // Size of the level used to create the next level.
        uint256 currentLevelSize = treeSize + leaves.length;

        // The index where changes begin at the next level.
        uint256 nextLevelStartIndex = currentLevelStartIndex >> 1;

        // The size of the next level.
        uint256 nextLevelSize = ((currentLevelSize - 1) >> 1) + 1;

        for (uint256 level = 0; level < treeDepth; ) {
            // The number of nodes for the new level that will be created,
            // only the new values, not the entire level.
            uint256 numberOfNewNodes = nextLevelSize - nextLevelStartIndex;
            uint256[] memory nextLevelNewNodes = new uint256[](numberOfNewNodes);
            for (uint256 i = 0; i < numberOfNewNodes; ) {
                uint256 leftNode;

                // Assign the left node using the saved path or the position in the array.
                if ((i + nextLevelStartIndex) * 2 < currentLevelStartIndex) {
                    leftNode = self.sideNodes[level];
                } else {
                    leftNode = currentLevelNewNodes[(i + nextLevelStartIndex) * 2 - currentLevelStartIndex];
                }

                uint256 rightNode;

                // Assign the right node if the value exists.
                if ((i + nextLevelStartIndex) * 2 + 1 < currentLevelSize) {
                    rightNode = currentLevelNewNodes[(i + nextLevelStartIndex) * 2 + 1 - currentLevelStartIndex];
                }

                uint256 parentNode;

                // Assign the parent node.
                // If it has a right child the result will be the hash(leftNode, rightNode) if not,
                // it will be the leftNode.
                if (rightNode != 0) {
                    parentNode = PoseidonT3.hash([leftNode, rightNode]);
                } else {
                    parentNode = leftNode;
                }

                nextLevelNewNodes[i] = parentNode;

                unchecked {
                    ++i;
                }
            }

            // Update the `sideNodes` variable.
            // If `currentLevelSize` is odd, the saved value will be the last value of the array
            // if it is even and there are more than 1 element in `currentLevelNewNodes`, the saved value
            // will be the value before the last one.
            // If it is even and there is only one element, there is no need to save anything because
            // the correct value for this level was already saved before.
            if (currentLevelSize & 1 == 1) {
                self.sideNodes[level] = currentLevelNewNodes[currentLevelNewNodes.length - 1];
            } else if (currentLevelNewNodes.length > 1) {
                self.sideNodes[level] = currentLevelNewNodes[currentLevelNewNodes.length - 2];
            }

            currentLevelStartIndex = nextLevelStartIndex;

            // Calculate the next level startIndex value.
            // It is the position of the parent node which is pos/2.
            nextLevelStartIndex >>= 1;

            // Update the next array that will be used to calculate the next level.
            currentLevelNewNodes = nextLevelNewNodes;

            currentLevelSize = nextLevelSize;

            // Calculate the size of the next level.
            // The size of the next level is (currentLevelSize - 1) / 2 + 1.
            nextLevelSize = ((nextLevelSize - 1) >> 1) + 1;

            unchecked {
                ++level;
            }
        }

        // Update tree size
        self.size = treeSize + leaves.length;

        // Update tree root
        self.sideNodes[treeDepth] = currentLevelNewNodes[0];

        return currentLevelNewNodes[0];
    }

    /// @dev Updates the value of an existing leaf and recalculates hashes
    /// to maintain tree integrity.
    /// @param self: A storage reference to the 'LeanIMTData' struct.
    /// @param oldLeaf: The value of the leaf that is to be updated.
    /// @param newLeaf: The new value that will replace the oldLeaf in the tree.
    /// @param siblingNodes: An array of sibling nodes that are necessary to recalculate the path to the root.
    /// @return The new hash of the updated node after the leaf has been updated.
    function _update(
        LeanIMTData storage self,
        uint256 oldLeaf,
        uint256 newLeaf,
        uint256[] calldata siblingNodes
    ) internal returns (uint256) {
        if (newLeaf >= SNARK_SCALAR_FIELD) {
            revert LeafGreaterThanSnarkScalarField();
        } else if (!_has(self, oldLeaf)) {
            revert LeafDoesNotExist();
        } else if (_has(self, newLeaf)) {
            revert LeafAlreadyExists();
        }

        uint256 index = _indexOf(self, oldLeaf);
        uint256 node = newLeaf;
        uint256 oldRoot = oldLeaf;

        uint256 lastIndex = self.size - 1;
        uint256 i = 0;

        // Cache tree depth to optimize gas
        uint256 treeDepth = self.depth;

        for (uint256 level = 0; level < treeDepth; ) {
            if ((index >> level) & 1 == 1) {
                if (siblingNodes[i] >= SNARK_SCALAR_FIELD) {
                    revert LeafGreaterThanSnarkScalarField();
                }

                node = PoseidonT3.hash([siblingNodes[i], node]);
                oldRoot = PoseidonT3.hash([siblingNodes[i], oldRoot]);

                unchecked {
                    ++i;
                }
            } else {
                if (index >> level != lastIndex >> level) {
                    if (siblingNodes[i] >= SNARK_SCALAR_FIELD) {
                        revert LeafGreaterThanSnarkScalarField();
                    }

                    node = PoseidonT3.hash([node, siblingNodes[i]]);
                    oldRoot = PoseidonT3.hash([oldRoot, siblingNodes[i]]);

                    unchecked {
                        ++i;
                    }
                } else {
                    self.sideNodes[i] = node;
                }
            }

            unchecked {
                ++level;
            }
        }

        if (oldRoot != _root(self)) {
            revert WrongSiblingNodes();
        }

        self.sideNodes[treeDepth] = node;

        if (newLeaf != 0) {
            self.leaves[newLeaf] = self.leaves[oldLeaf];
        }

        self.leaves[oldLeaf] = 0;

        return node;
    }

    /// @dev Removes a leaf from the tree by setting its value to zero.
    /// This function utilizes the update function to set the leaf's value
    /// to zero and update the tree's state accordingly.
    /// @param self: A storage reference to the 'LeanIMTData' struct.
    /// @param oldLeaf: The value of the leaf to be removed.
    /// @param siblingNodes: An array of sibling nodes required for updating the path to the root after removal.
    /// @return The new root hash of the tree after the leaf has been removed.
    function _remove(
        LeanIMTData storage self,
        uint256 oldLeaf,
        uint256[] calldata siblingNodes
    ) internal returns (uint256) {
        return _update(self, oldLeaf, 0, siblingNodes);
    }

    /// @dev Checks if a leaf exists in the tree.
    /// @param self: A storage reference to the 'LeanIMTData' struct.
    /// @param leaf: The value of the leaf to check for existence.
    /// @return A boolean value indicating whether the leaf exists in the tree.
    function _has(LeanIMTData storage self, uint256 leaf) internal view returns (bool) {
        return self.leaves[leaf] != 0;
    }

    /// @dev Retrieves the index of a given leaf in the tree.
    /// @param self: A storage reference to the 'LeanIMTData' struct.
    /// @param leaf: The value of the leaf whose index is to be found.
    /// @return The index of the specified leaf within the tree. If the leaf is not present, the function
    /// reverts with a custom error.
    function _indexOf(LeanIMTData storage self, uint256 leaf) internal view returns (uint256) {
        if (self.leaves[leaf] == 0) {
            revert LeafDoesNotExist();
        }

        return self.leaves[leaf] - 1;
    }

    /// @dev Retrieves the root of the tree from the 'sideNodes' mapping using the
    /// current tree depth.
    /// @param self: A storage reference to the 'LeanIMTData' struct.
    /// @return The root hash of the tree.
    function _root(LeanIMTData storage self) internal view returns (uint256) {
        return self.sideNodes[self.depth];
    }
}

// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.28;

import {IERC20} from '@oz/interfaces/IERC20.sol';

import {ProofLib} from '../contracts/lib/ProofLib.sol';
import {IPrivacyPool} from 'interfaces/IPrivacyPool.sol';

/**
 * @title IEntrypoint
 * @notice Interface for the Entrypoint contract
 */
interface IEntrypoint {
  /*///////////////////////////////////////////////////////////////
                              STRUCTS
  //////////////////////////////////////////////////////////////*/

  /**
   * @notice Struct for the asset configuration
   * @param pool The Privacy Pool contracts for the asset
   * @param minimumDepositAmount The minimum amount that can be deposited
   * @param vettingfeeBPS The deposit fee in basis points
   */
  struct AssetConfig {
    IPrivacyPool pool;
    uint256 minimumDepositAmount;
    uint256 vettingFeeBPS;
    uint256 maxRelayFeeBPS;
  }

  /**
   * @notice Struct for the relay data
   * @param recipient The recipient of the funds withdrawn from the pool
   * @param feeRecipient The recipient of the fee
   * @param relayfeeBPS The relay fee in basis points
   */
  struct RelayData {
    address recipient;
    address feeRecipient;
    uint256 relayFeeBPS;
  }

  /**
   * @notice Struct for the onchain association set data
   * @param root The ASP root
   * @param ipfsCID The IPFS v1 CID of the ASP data. A content-addressed identifier computed by hashing
   *                the content with SHA-256, adding multicodec/multihash prefixes, and encoding in base32/58.
   *                This uniquely identifies data by its content rather than location.
   * @param timestamp The timestamp on which the root was updated
   */
  struct AssociationSetData {
    uint256 root;
    string ipfsCID;
    uint256 timestamp;
  }

  /*///////////////////////////////////////////////////////////////
                              EVENTS
  //////////////////////////////////////////////////////////////*/

  /**
   * @notice Emitted when pushing a new root to the association root set
   * @param _root The latest ASP root
   * @param _ipfsCID The IPFS CID of the association set data
   * @param _timestamp The timestamp of root update
   */
  event RootUpdated(uint256 _root, string _ipfsCID, uint256 _timestamp);

  /**
   * @notice Emitted when pushing a new root to the association root set
   * @param _depositor The address of the depositor
   * @param _pool The Privacy Pool contract
   * @param _commitment The commitment hash for the deposit
   * @param _amount The amount of asset deposited
   */
  event Deposited(address indexed _depositor, IPrivacyPool indexed _pool, uint256 _commitment, uint256 _amount);

  /**
   * @notice Emitted when processing a withdrawal through the Entrypoint
   * @param _relayer The address of the relayer
   * @param _recipient The address of the withdrawal recipient
   * @param _asset The asset being withdrawn
   * @param _amount The amount of asset withdrawn
   * @param _feeAmount The fee paid to the relayer
   */
  event WithdrawalRelayed(
    address indexed _relayer, address indexed _recipient, IERC20 indexed _asset, uint256 _amount, uint256 _feeAmount
  );

  /**
   * @notice Emitted when withdrawing fees from the Entrypoint
   * @param _asset The asset being withdrawn
   * @param _recipient The address of the fees withdrawal recipient
   * @param _amount The amount of asset withdrawn
   */
  event FeesWithdrawn(IERC20 _asset, address _recipient, uint256 _amount);

  /**
   * @notice Emitted when winding down a Privacy Pool
   * @param _pool The Privacy Pool contract
   */
  event PoolWindDown(IPrivacyPool _pool);

  /**
   * @notice Emitted when registering a Privacy Pool in the Entrypoint registry
   * @param _pool The Privacy Pool contract
   * @param _asset The asset of the pool
   * @param _scope The unique scope of the pool
   */
  event PoolRegistered(IPrivacyPool _pool, IERC20 _asset, uint256 _scope);

  /**
   * @notice Emitted when removing a Privacy Pool from the Entrypoint registry
   * @param _pool The Privacy Pool contract
   * @param _asset The asset of the pool
   * @param _scope The unique scope of the pool
   */
  event PoolRemoved(IPrivacyPool _pool, IERC20 _asset, uint256 _scope);

  /**
   * @notice Emitted when updating the configuration of a Privacy Pool
   * @param _pool The Privacy Pool contract
   * @param _asset The asset of the pool
   * @param _newMinimumDepositAmount The updated minimum deposit amount
   * @param _newVettingFeeBPS The updated vetting fee in basis points
   * @param _newMaxRelayFeeBPS The updated maximum relay fee in basis points
   */
  event PoolConfigurationUpdated(
    IPrivacyPool _pool,
    IERC20 _asset,
    uint256 _newMinimumDepositAmount,
    uint256 _newVettingFeeBPS,
    uint256 _newMaxRelayFeeBPS
  );

  /*///////////////////////////////////////////////////////////////
                              ERRORS
  //////////////////////////////////////////////////////////////*/

  /**
   * @notice Thrown when trying to withdraw an invalid amount
   */
  error InvalidWithdrawalAmount();

  /**
   * @notice Thrown when trying to access a non-existent pool
   */
  error PoolNotFound();

  /**
   * @notice Thrown when trying to register a dead pool
   */
  error PoolIsDead();

  /**
   * @notice Thrown when trying to register a pool whose configured Entrypoint is not this one
   */
  error InvalidEntrypointForPool();

  /**
   * @notice Thrown when trying to register a pool for an asset that is already present in the registry
   */
  error AssetPoolAlreadyRegistered();

  /**
   * @notice Thrown when trying to register a pool for a scope that is already present in the registry
   */
  error ScopePoolAlreadyRegistered();

  /**
   * @notice Thrown when trying to deposit less than the minimum deposit amount
   */
  error MinimumDepositAmount();

  /**
   * @notice Thrown when trying to relay with a relayer fee greater than the maximum configured
   */
  error RelayFeeGreaterThanMax();

  /**
   * @notice Thrown when trying to process a withdrawal with an invalid processooor
   */
  error InvalidProcessooor();

  /**
   * @notice Thrown when finding an invalid state in the pool like an invalid asset balance
   */
  error InvalidPoolState();

  /**
   * @notice Thrown when trying to push a an IPFS CID with an invalid length
   */
  error InvalidIPFSCIDLength();

  /**
   * @notice Thrown when trying to push a root with an empty root
   */
  error EmptyRoot();

  /**
   * @notice Thrown when failing to send the native asset to an account
   */
  error NativeAssetTransferFailed();

  /**
   * @notice Thrown when an address parameter is zero
   */
  error ZeroAddress();

  /**
   * @notice Thrown when a fee in basis points is greater than 10000 (100%)
   */
  error InvalidFeeBPS();

  /**
   * @notice Thrown when trying to access an association set at an invalid index
   */
  error InvalidIndex();

  /**
   * @notice Thrown when trying to get the latest root when no roots exist
   */
  error NoRootsAvailable();

  /**
   * @notice Thrown when trying to register a pool with an asset that doesn't match the pool's asset
   */
  error AssetMismatch();

  /**
   * @notice Thrown when trying to send native asset to the Entrypoint
   */
  error NativeAssetNotAccepted();

  /*//////////////////////////////////////////////////////////////
                                LOGIC
  //////////////////////////////////////////////////////////////*/

  /**
   * @notice Initializes the contract state
   * @param _owner The initial owner
   * @param _postman The initial postman
   */
  function initialize(address _owner, address _postman) external;

  /**
   * @notice Push a new root to the association root set
   * @param _root The new ASP root
   * @param _ipfsCID The IPFS v1 CID of the association set data
   * @return _index The index of the newly added root
   */
  function updateRoot(uint256 _root, string memory _ipfsCID) external returns (uint256 _index);

  /**
   * @notice Make a native asset deposit into the Privacy Pool
   * @param _precommitment The precommitment for the deposit
   * @return _commitment The deposit commitment hash
   */
  function deposit(uint256 _precommitment) external payable returns (uint256 _commitment);

  /**
   * @notice Make an ERC20 deposit into the Privacy Pool
   * @param _asset The asset to deposit
   * @param _value The amount of asset to deposit
   * @param _precommitment The precommitment for the deposit
   * @return _commitment The deposit commitment hash
   */
  function deposit(IERC20 _asset, uint256 _value, uint256 _precommitment) external returns (uint256 _commitment);

  /**
   * @notice Process a withdrawal
   * @param _withdrawal The `Withdrawal` struct
   * @param _proof The `WithdrawProof` struct containing the withdarawal proof signals
   * @param _scope The Pool scope to withdraw from
   */
  function relay(
    IPrivacyPool.Withdrawal calldata _withdrawal,
    ProofLib.WithdrawProof calldata _proof,
    uint256 _scope
  ) external;

  /**
   * @notice Register a Privacy Pool in the registry
   * @param _asset The asset of the pool
   * @param _pool The address of the Privacy Pool contract
   * @param _minimumDepositAmount The minimum deposit amount for the asset
   * @param _vettingFeeBPS The deposit fee in basis points
   * @param _maxRelayFeeBPS The maximum relay fee in basis points
   */
  function registerPool(
    IERC20 _asset,
    IPrivacyPool _pool,
    uint256 _minimumDepositAmount,
    uint256 _vettingFeeBPS,
    uint256 _maxRelayFeeBPS
  ) external;

  /**
   * @notice Remove a Privacy Pool from the registry
   * @param _asset The asset of the pool
   */
  function removePool(IERC20 _asset) external;

  /**
   * @notice Updates the configuration of a specific pool
   * @param _asset The asset of the pool to update
   * @param _minimumDepositAmount The new minimum deposit amount
   * @param _vettingFeeBPS The new vetting fee in basis points
   * @param _maxRelayFeeBPS The new max relay fee in basis points
   */
  function updatePoolConfiguration(
    IERC20 _asset,
    uint256 _minimumDepositAmount,
    uint256 _vettingFeeBPS,
    uint256 _maxRelayFeeBPS
  ) external;

  /**
   * @notice Irreversebly halt deposits from a Privacy Pool
   * @param _pool The Privacy Pool contract
   */
  function windDownPool(IPrivacyPool _pool) external;

  /**
   * @notice Withdraw fees from the Entrypoint
   * @param _asset The asset to withdraw
   * @param _recipient The recipient of the fees
   */
  function withdrawFees(IERC20 _asset, address _recipient) external;

  /*///////////////////////////////////////////////////////////////
                            VIEWS
  //////////////////////////////////////////////////////////////*/

  /**
   * @notice Returns the configured pool for a scope
   * @param _scope The unique scope of the pool
   * @return _pool The Privacy Pool contract
   */
  function scopeToPool(uint256 _scope) external view returns (IPrivacyPool _pool);

  /**
   * @notice Returns the configuration for an asset
   * @param _asset The asset address
   * @return _pool The Privacy Pool contract
   * @return _minimumDepositAmount The minimum deposit amount
   * @return _vettingFeeBPS The deposit fee in basis points
   * @return _maxRelayFeeBPS The max relayer fee in basis points
   */
  function assetConfig(IERC20 _asset)
    external
    view
    returns (IPrivacyPool _pool, uint256 _minimumDepositAmount, uint256 _vettingFeeBPS, uint256 _maxRelayFeeBPS);

  /**
   * @notice Returns the association set data at an index
   * @param _index The index of the array
   * @return _root The updated ASP root
   * @return _ipfsCID The IPFS v1 CID for the association set data
   * @return _timestamp The timestamp of the root update
   */
  function associationSets(uint256 _index)
    external
    view
    returns (uint256 _root, string memory _ipfsCID, uint256 _timestamp);

  /**
   * @notice Returns the latest ASP root
   * @return _root The latest ASP root
   */
  function latestRoot() external view returns (uint256 _root);

  /**
   * @notice Returns an ASP root by index
   * @param _index The index
   * @return _root The ASP root at the index
   */
  function rootByIndex(uint256 _index) external view returns (uint256 _root);
}

// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.28;

/**
 * @title IVerifier
 * @notice Interface of the Groth16 verifier contracts
 */
interface IVerifier {
  /**
   * @notice Verifies a Withdrawal Proof
   * @param _pA First elliptic curve point (π_A) of the Groth16 proof, encoded as two field elements
   * @param _pB Second elliptic curve point (π_B) of the Groth16 proof, encoded as 2x2 matrix of field elements
   * @param _pC Third elliptic curve point (π_C) of the Groth16 proof, encoded as two field elements
   * @param _pubSignals The proof public signals (both input and output)
   * @return _valid The boolean indicating if the proof is valid
   */
  function verifyProof(
    uint256[2] memory _pA,
    uint256[2][2] memory _pB,
    uint256[2] memory _pC,
    uint256[8] memory _pubSignals
  ) external returns (bool _valid);

  /**
   * @notice Verifies a Ragequit Proof
   * @param _pA First elliptic curve point (π_A) of the Groth16 proof, encoded as two field elements
   * @param _pB Second elliptic curve point (π_B) of the Groth16 proof, encoded as 2x2 matrix of field elements
   * @param _pC Third elliptic curve point (π_C) of the Groth16 proof, encoded as two field elements
   * @param _pubSignals The proof public signals (both input and output)
   * @return _valid The boolean indicating if the proof is valid
   */
  function verifyProof(
    uint256[2] memory _pA,
    uint256[2][2] memory _pB,
    uint256[2] memory _pC,
    uint256[4] memory _pubSignals
  ) external returns (bool _valid);
}

/// SPDX-License-Identifier: MIT
pragma solidity >=0.7.0;

library PoseidonT3 {
  uint constant M00 = 0x109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b;
  uint constant M01 = 0x2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771;
  uint constant M02 = 0x143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7;
  uint constant M10 = 0x16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e0;
  uint constant M11 = 0x2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe23;
  uint constant M12 = 0x176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee2911;

  // See here for a simplified implementation: https://github.com/vimwitch/poseidon-solidity/blob/e57becdabb65d99fdc586fe1e1e09e7108202d53/contracts/Poseidon.sol#L40
  // Inspired by: https://github.com/iden3/circomlibjs/blob/v0.0.8/src/poseidon_slow.js
  function hash(uint[2] memory) public pure returns (uint) {
    assembly {
      let F := 21888242871839275222246405745257275088548364400416034343698204186575808495617
      let M20 := 0x2b90bba00fca0589f617e7dcbfe82e0df706ab640ceb247b791a93b74e36736d
      let M21 := 0x101071f0032379b697315876690f053d148d4e109f5fb065c8aacc55a0f89bfa
      let M22 := 0x19a3fc0a56702bf417ba7fee3802593fa644470307043f7773279cd71d25d5e0

      // load the inputs from memory
      let state1 := add(mod(mload(0x80), F), 0x00f1445235f2148c5986587169fc1bcd887b08d4d00868df5696fff40956e864)
      let state2 := add(mod(mload(0xa0), F), 0x08dff3487e8ac99e1f29a058d0fa80b930c728730b7ab36ce879f3890ecf73f5)
      let scratch0 := mulmod(state1, state1, F)
      state1 := mulmod(mulmod(scratch0, scratch0, F), state1, F)
      scratch0 := mulmod(state2, state2, F)
      state2 := mulmod(mulmod(scratch0, scratch0, F), state2, F)
      scratch0 := add(
        0x2f27be690fdaee46c3ce28f7532b13c856c35342c84bda6e20966310fadc01d0,
        add(add(15452833169820924772166449970675545095234312153403844297388521437673434406763, mulmod(state1, M10, F)), mulmod(state2, M20, F))
      )
      let scratch1 := add(
        0x2b2ae1acf68b7b8d2416bebf3d4f6234b763fe04b8043ee48b8327bebca16cf2,
        add(add(18674271267752038776579386132900109523609358935013267566297499497165104279117, mulmod(state1, M11, F)), mulmod(state2, M21, F))
      )
      let scratch2 := add(
        0x0319d062072bef7ecca5eac06f97d4d55952c175ab6b03eae64b44c7dbf11cfa,
        add(add(14817777843080276494683266178512808687156649753153012854386334860566696099579, mulmod(state1, M12, F)), mulmod(state2, M22, F))
      )
      let state0 := mulmod(scratch0, scratch0, F)
      scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)
      state0 := mulmod(scratch1, scratch1, F)
      scratch1 := mulmod(mulmod(state0, state0, F), scratch1, F)
      state0 := mulmod(scratch2, scratch2, F)
      scratch2 := mulmod(mulmod(state0, state0, F), scratch2, F)
      state0 := add(0x28813dcaebaeaa828a376df87af4a63bc8b7bf27ad49c6298ef7b387bf28526d, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))
      state1 := add(0x2727673b2ccbc903f181bf38e1c1d40d2033865200c352bc150928adddf9cb78, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))
      state2 := add(0x234ec45ca27727c2e74abd2b2a1494cd6efbd43e340587d6b8fb9e31e65cc632, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))
      scratch0 := mulmod(state0, state0, F)
      state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)
      scratch0 := mulmod(state1, state1, F)
      state1 := mulmod(mulmod(scratch0, scratch0, F), state1, F)
      scratch0 := mulmod(state2, state2, F)
      state2 := mulmod(mulmod(scratch0, scratch0, F), state2, F)
      scratch0 := add(0x15b52534031ae18f7f862cb2cf7cf760ab10a8150a337b1ccd99ff6e8797d428, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))
      scratch1 := add(0x0dc8fad6d9e4b35f5ed9a3d186b79ce38e0e8a8d1b58b132d701d4eecf68d1f6, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))
      scratch2 := add(0x1bcd95ffc211fbca600f705fad3fb567ea4eb378f62e1fec97805518a47e4d9c, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))
      state0 := mulmod(scratch0, scratch0, F)
      scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)
      state0 := mulmod(scratch1, scratch1, F)
      scratch1 := mulmod(mulmod(state0, state0, F), scratch1, F)
      state0 := mulmod(scratch2, scratch2, F)
      scratch2 := mulmod(mulmod(state0, state0, F), scratch2, F)
      state0 := add(0x10520b0ab721cadfe9eff81b016fc34dc76da36c2578937817cb978d069de559, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))
      state1 := add(0x1f6d48149b8e7f7d9b257d8ed5fbbaf42932498075fed0ace88a9eb81f5627f6, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))
      state2 := add(0x1d9655f652309014d29e00ef35a2089bfff8dc1c816f0dc9ca34bdb5460c8705, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))
      scratch0 := mulmod(state0, state0, F)
      state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)
      scratch0 := add(0x04df5a56ff95bcafb051f7b1cd43a99ba731ff67e47032058fe3d4185697cc7d, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))
      scratch1 := add(0x0672d995f8fff640151b3d290cedaf148690a10a8c8424a7f6ec282b6e4be828, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))
      scratch2 := add(0x099952b414884454b21200d7ffafdd5f0c9a9dcc06f2708e9fc1d8209b5c75b9, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))
      state0 := mulmod(scratch0, scratch0, F)
      scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)
      state0 := add(0x052cba2255dfd00c7c483143ba8d469448e43586a9b4cd9183fd0e843a6b9fa6, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))
      state1 := add(0x0b8badee690adb8eb0bd74712b7999af82de55707251ad7716077cb93c464ddc, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))
      state2 := add(0x119b1590f13307af5a1ee651020c07c749c15d60683a8050b963d0a8e4b2bdd1, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))
      scratch0 := mulmod(state0, state0, F)
      state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)
      scratch0 := add(0x03150b7cd6d5d17b2529d36be0f67b832c4acfc884ef4ee5ce15be0bfb4a8d09, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))
      scratch1 := add(0x2cc6182c5e14546e3cf1951f173912355374efb83d80898abe69cb317c9ea565, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))
      scratch2 := add(0x005032551e6378c450cfe129a404b3764218cadedac14e2b92d2cd73111bf0f9, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))
      state0 := mulmod(scratch0, scratch0, F)
      scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)
      state0 := add(0x233237e3289baa34bb147e972ebcb9516469c399fcc069fb88f9da2cc28276b5, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))
      state1 := add(0x05c8f4f4ebd4a6e3c980d31674bfbe6323037f21b34ae5a4e80c2d4c24d60280, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))
      state2 := add(0x0a7b1db13042d396ba05d818a319f25252bcf35ef3aeed91ee1f09b2590fc65b, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))
      scratch0 := mulmod(state0, state0, F)
      state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)
      scratch0 := add(0x2a73b71f9b210cf5b14296572c9d32dbf156e2b086ff47dc5df542365a404ec0, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))
      scratch1 := add(0x1ac9b0417abcc9a1935107e9ffc91dc3ec18f2c4dbe7f22976a760bb5c50c460, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))
      scratch2 := add(0x12c0339ae08374823fabb076707ef479269f3e4d6cb104349015ee046dc93fc0, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))
      state0 := mulmod(scratch0, scratch0, F)
      scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)
      state0 := add(0x0b7475b102a165ad7f5b18db4e1e704f52900aa3253baac68246682e56e9a28e, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))
      state1 := add(0x037c2849e191ca3edb1c5e49f6e8b8917c843e379366f2ea32ab3aa88d7f8448, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))
      state2 := add(0x05a6811f8556f014e92674661e217e9bd5206c5c93a07dc145fdb176a716346f, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))
      scratch0 := mulmod(state0, state0, F)
      state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)
      scratch0 := add(0x29a795e7d98028946e947b75d54e9f044076e87a7b2883b47b675ef5f38bd66e, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))
      scratch1 := add(0x20439a0c84b322eb45a3857afc18f5826e8c7382c8a1585c507be199981fd22f, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))
      scratch2 := add(0x2e0ba8d94d9ecf4a94ec2050c7371ff1bb50f27799a84b6d4a2a6f2a0982c887, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))
      state0 := mulmod(scratch0, scratch0, F)
      scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)
      state0 := add(0x143fd115ce08fb27ca38eb7cce822b4517822cd2109048d2e6d0ddcca17d71c8, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))
      state1 := add(0x0c64cbecb1c734b857968dbbdcf813cdf8611659323dbcbfc84323623be9caf1, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))
      state2 := add(0x028a305847c683f646fca925c163ff5ae74f348d62c2b670f1426cef9403da53, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))
      scratch0 := mulmod(state0, state0, F)
      state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)
      scratch0 := add(0x2e4ef510ff0b6fda5fa940ab4c4380f26a6bcb64d89427b824d6755b5db9e30c, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))
      scratch1 := add(0x0081c95bc43384e663d79270c956ce3b8925b4f6d033b078b96384f50579400e, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))
      scratch2 := add(0x2ed5f0c91cbd9749187e2fade687e05ee2491b349c039a0bba8a9f4023a0bb38, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))
      state0 := mulmod(scratch0, scratch0, F)
      scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)
      state0 := add(0x30509991f88da3504bbf374ed5aae2f03448a22c76234c8c990f01f33a735206, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))
      state1 := add(0x1c3f20fd55409a53221b7c4d49a356b9f0a1119fb2067b41a7529094424ec6ad, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))
      state2 := add(0x10b4e7f3ab5df003049514459b6e18eec46bb2213e8e131e170887b47ddcb96c, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))
      scratch0 := mulmod(state0, state0, F)
      state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)
      scratch0 := add(0x2a1982979c3ff7f43ddd543d891c2abddd80f804c077d775039aa3502e43adef, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))
      scratch1 := add(0x1c74ee64f15e1db6feddbead56d6d55dba431ebc396c9af95cad0f1315bd5c91, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))
      scratch2 := add(0x07533ec850ba7f98eab9303cace01b4b9e4f2e8b82708cfa9c2fe45a0ae146a0, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))
      state0 := mulmod(scratch0, scratch0, F)
      scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)
      state0 := add(0x21576b438e500449a151e4eeaf17b154285c68f42d42c1808a11abf3764c0750, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))
      state1 := add(0x2f17c0559b8fe79608ad5ca193d62f10bce8384c815f0906743d6930836d4a9e, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))
      state2 := add(0x2d477e3862d07708a79e8aae946170bc9775a4201318474ae665b0b1b7e2730e, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))
      scratch0 := mulmod(state0, state0, F)
      state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)
      scratch0 := add(0x162f5243967064c390e095577984f291afba2266c38f5abcd89be0f5b2747eab, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))
      scratch1 := add(0x2b4cb233ede9ba48264ecd2c8ae50d1ad7a8596a87f29f8a7777a70092393311, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))
      scratch2 := add(0x2c8fbcb2dd8573dc1dbaf8f4622854776db2eece6d85c4cf4254e7c35e03b07a, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))
      state0 := mulmod(scratch0, scratch0, F)
      scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)
      state0 := add(0x1d6f347725e4816af2ff453f0cd56b199e1b61e9f601e9ade5e88db870949da9, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))
      state1 := add(0x204b0c397f4ebe71ebc2d8b3df5b913df9e6ac02b68d31324cd49af5c4565529, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))
      state2 := add(0x0c4cb9dc3c4fd8174f1149b3c63c3c2f9ecb827cd7dc25534ff8fb75bc79c502, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))
      scratch0 := mulmod(state0, state0, F)
      state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)
      scratch0 := add(0x174ad61a1448c899a25416474f4930301e5c49475279e0639a616ddc45bc7b54, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))
      scratch1 := add(0x1a96177bcf4d8d89f759df4ec2f3cde2eaaa28c177cc0fa13a9816d49a38d2ef, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))
      scratch2 := add(0x066d04b24331d71cd0ef8054bc60c4ff05202c126a233c1a8242ace360b8a30a, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))
      state0 := mulmod(scratch0, scratch0, F)
      scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)
      state0 := add(0x2a4c4fc6ec0b0cf52195782871c6dd3b381cc65f72e02ad527037a62aa1bd804, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))
      state1 := add(0x13ab2d136ccf37d447e9f2e14a7cedc95e727f8446f6d9d7e55afc01219fd649, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))
      state2 := add(0x1121552fca26061619d24d843dc82769c1b04fcec26f55194c2e3e869acc6a9a, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))
      scratch0 := mulmod(state0, state0, F)
      state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)
      scratch0 := add(0x00ef653322b13d6c889bc81715c37d77a6cd267d595c4a8909a5546c7c97cff1, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))
      scratch1 := add(0x0e25483e45a665208b261d8ba74051e6400c776d652595d9845aca35d8a397d3, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))
      scratch2 := add(0x29f536dcb9dd7682245264659e15d88e395ac3d4dde92d8c46448db979eeba89, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))
      state0 := mulmod(scratch0, scratch0, F)
      scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)
      state0 := add(0x2a56ef9f2c53febadfda33575dbdbd885a124e2780bbea170e456baace0fa5be, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))
      state1 := add(0x1c8361c78eb5cf5decfb7a2d17b5c409f2ae2999a46762e8ee416240a8cb9af1, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))
      state2 := add(0x151aff5f38b20a0fc0473089aaf0206b83e8e68a764507bfd3d0ab4be74319c5, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))
      scratch0 := mulmod(state0, state0, F)
      state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)
      scratch0 := add(0x04c6187e41ed881dc1b239c88f7f9d43a9f52fc8c8b6cdd1e76e47615b51f100, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))
      scratch1 := add(0x13b37bd80f4d27fb10d84331f6fb6d534b81c61ed15776449e801b7ddc9c2967, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))
      scratch2 := add(0x01a5c536273c2d9df578bfbd32c17b7a2ce3664c2a52032c9321ceb1c4e8a8e4, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))
      state0 := mulmod(scratch0, scratch0, F)
      scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)
      state0 := add(0x2ab3561834ca73835ad05f5d7acb950b4a9a2c666b9726da832239065b7c3b02, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))
      state1 := add(0x1d4d8ec291e720db200fe6d686c0d613acaf6af4e95d3bf69f7ed516a597b646, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))
      state2 := add(0x041294d2cc484d228f5784fe7919fd2bb925351240a04b711514c9c80b65af1d, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))
      scratch0 := mulmod(state0, state0, F)
      state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)
      scratch0 := add(0x154ac98e01708c611c4fa715991f004898f57939d126e392042971dd90e81fc6, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))
      scratch1 := add(0x0b339d8acca7d4f83eedd84093aef51050b3684c88f8b0b04524563bc6ea4da4, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))
      scratch2 := add(0x0955e49e6610c94254a4f84cfbab344598f0e71eaff4a7dd81ed95b50839c82e, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))
      state0 := mulmod(scratch0, scratch0, F)
      scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)
      state0 := add(0x06746a6156eba54426b9e22206f15abca9a6f41e6f535c6f3525401ea0654626, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))
      state1 := add(0x0f18f5a0ecd1423c496f3820c549c27838e5790e2bd0a196ac917c7ff32077fb, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))
      state2 := add(0x04f6eeca1751f7308ac59eff5beb261e4bb563583ede7bc92a738223d6f76e13, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))
      scratch0 := mulmod(state0, state0, F)
      state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)
      scratch0 := add(0x2b56973364c4c4f5c1a3ec4da3cdce038811eb116fb3e45bc1768d26fc0b3758, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))
      scratch1 := add(0x123769dd49d5b054dcd76b89804b1bcb8e1392b385716a5d83feb65d437f29ef, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))
      scratch2 := add(0x2147b424fc48c80a88ee52b91169aacea989f6446471150994257b2fb01c63e9, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))
      state0 := mulmod(scratch0, scratch0, F)
      scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)
      state0 := add(0x0fdc1f58548b85701a6c5505ea332a29647e6f34ad4243c2ea54ad897cebe54d, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))
      state1 := add(0x12373a8251fea004df68abcf0f7786d4bceff28c5dbbe0c3944f685cc0a0b1f2, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))
      state2 := add(0x21e4f4ea5f35f85bad7ea52ff742c9e8a642756b6af44203dd8a1f35c1a90035, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))
      scratch0 := mulmod(state0, state0, F)
      state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)
      scratch0 := add(0x16243916d69d2ca3dfb4722224d4c462b57366492f45e90d8a81934f1bc3b147, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))
      scratch1 := add(0x1efbe46dd7a578b4f66f9adbc88b4378abc21566e1a0453ca13a4159cac04ac2, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))
      scratch2 := add(0x07ea5e8537cf5dd08886020e23a7f387d468d5525be66f853b672cc96a88969a, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))
      state0 := mulmod(scratch0, scratch0, F)
      scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)
      state0 := add(0x05a8c4f9968b8aa3b7b478a30f9a5b63650f19a75e7ce11ca9fe16c0b76c00bc, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))
      state1 := add(0x20f057712cc21654fbfe59bd345e8dac3f7818c701b9c7882d9d57b72a32e83f, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))
      state2 := add(0x04a12ededa9dfd689672f8c67fee31636dcd8e88d01d49019bd90b33eb33db69, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))
      scratch0 := mulmod(state0, state0, F)
      state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)
      scratch0 := add(0x27e88d8c15f37dcee44f1e5425a51decbd136ce5091a6767e49ec9544ccd101a, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))
      scratch1 := add(0x2feed17b84285ed9b8a5c8c5e95a41f66e096619a7703223176c41ee433de4d1, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))
      scratch2 := add(0x1ed7cc76edf45c7c404241420f729cf394e5942911312a0d6972b8bd53aff2b8, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))
      state0 := mulmod(scratch0, scratch0, F)
      scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)
      state0 := add(0x15742e99b9bfa323157ff8c586f5660eac6783476144cdcadf2874be45466b1a, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))
      state1 := add(0x1aac285387f65e82c895fc6887ddf40577107454c6ec0317284f033f27d0c785, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))
      state2 := add(0x25851c3c845d4790f9ddadbdb6057357832e2e7a49775f71ec75a96554d67c77, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))
      scratch0 := mulmod(state0, state0, F)
      state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)
      scratch0 := add(0x15a5821565cc2ec2ce78457db197edf353b7ebba2c5523370ddccc3d9f146a67, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))
      scratch1 := add(0x2411d57a4813b9980efa7e31a1db5966dcf64f36044277502f15485f28c71727, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))
      scratch2 := add(0x002e6f8d6520cd4713e335b8c0b6d2e647e9a98e12f4cd2558828b5ef6cb4c9b, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))
      state0 := mulmod(scratch0, scratch0, F)
      scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)
      state0 := add(0x2ff7bc8f4380cde997da00b616b0fcd1af8f0e91e2fe1ed7398834609e0315d2, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))
      state1 := add(0x00b9831b948525595ee02724471bcd182e9521f6b7bb68f1e93be4febb0d3cbe, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))
      state2 := add(0x0a2f53768b8ebf6a86913b0e57c04e011ca408648a4743a87d77adbf0c9c3512, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))
      scratch0 := mulmod(state0, state0, F)
      state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)
      scratch0 := add(0x00248156142fd0373a479f91ff239e960f599ff7e94be69b7f2a290305e1198d, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))
      scratch1 := add(0x171d5620b87bfb1328cf8c02ab3f0c9a397196aa6a542c2350eb512a2b2bcda9, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))
      scratch2 := add(0x170a4f55536f7dc970087c7c10d6fad760c952172dd54dd99d1045e4ec34a808, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))
      state0 := mulmod(scratch0, scratch0, F)
      scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)
      state0 := add(0x29aba33f799fe66c2ef3134aea04336ecc37e38c1cd211ba482eca17e2dbfae1, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))
      state1 := add(0x1e9bc179a4fdd758fdd1bb1945088d47e70d114a03f6a0e8b5ba650369e64973, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))
      state2 := add(0x1dd269799b660fad58f7f4892dfb0b5afeaad869a9c4b44f9c9e1c43bdaf8f09, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))
      scratch0 := mulmod(state0, state0, F)
      state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)
      scratch0 := add(0x22cdbc8b70117ad1401181d02e15459e7ccd426fe869c7c95d1dd2cb0f24af38, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))
      scratch1 := add(0x0ef042e454771c533a9f57a55c503fcefd3150f52ed94a7cd5ba93b9c7dacefd, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))
      scratch2 := add(0x11609e06ad6c8fe2f287f3036037e8851318e8b08a0359a03b304ffca62e8284, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))
      state0 := mulmod(scratch0, scratch0, F)
      scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)
      state0 := add(0x1166d9e554616dba9e753eea427c17b7fecd58c076dfe42708b08f5b783aa9af, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))
      state1 := add(0x2de52989431a859593413026354413db177fbf4cd2ac0b56f855a888357ee466, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))
      state2 := add(0x3006eb4ffc7a85819a6da492f3a8ac1df51aee5b17b8e89d74bf01cf5f71e9ad, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))
      scratch0 := mulmod(state0, state0, F)
      state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)
      scratch0 := add(0x2af41fbb61ba8a80fdcf6fff9e3f6f422993fe8f0a4639f962344c8225145086, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))
      scratch1 := add(0x119e684de476155fe5a6b41a8ebc85db8718ab27889e85e781b214bace4827c3, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))
      scratch2 := add(0x1835b786e2e8925e188bea59ae363537b51248c23828f047cff784b97b3fd800, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))
      state0 := mulmod(scratch0, scratch0, F)
      scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)
      state0 := add(0x28201a34c594dfa34d794996c6433a20d152bac2a7905c926c40e285ab32eeb6, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))
      state1 := add(0x083efd7a27d1751094e80fefaf78b000864c82eb571187724a761f88c22cc4e7, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))
      state2 := add(0x0b6f88a3577199526158e61ceea27be811c16df7774dd8519e079564f61fd13b, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))
      scratch0 := mulmod(state0, state0, F)
      state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)
      scratch0 := add(0x0ec868e6d15e51d9644f66e1d6471a94589511ca00d29e1014390e6ee4254f5b, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))
      scratch1 := add(0x2af33e3f866771271ac0c9b3ed2e1142ecd3e74b939cd40d00d937ab84c98591, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))
      scratch2 := add(0x0b520211f904b5e7d09b5d961c6ace7734568c547dd6858b364ce5e47951f178, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))
      state0 := mulmod(scratch0, scratch0, F)
      scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)
      state0 := add(0x0b2d722d0919a1aad8db58f10062a92ea0c56ac4270e822cca228620188a1d40, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))
      state1 := add(0x1f790d4d7f8cf094d980ceb37c2453e957b54a9991ca38bbe0061d1ed6e562d4, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))
      state2 := add(0x0171eb95dfbf7d1eaea97cd385f780150885c16235a2a6a8da92ceb01e504233, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))
      scratch0 := mulmod(state0, state0, F)
      state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)
      scratch0 := add(0x0c2d0e3b5fd57549329bf6885da66b9b790b40defd2c8650762305381b168873, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))
      scratch1 := add(0x1162fb28689c27154e5a8228b4e72b377cbcafa589e283c35d3803054407a18d, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))
      scratch2 := add(0x2f1459b65dee441b64ad386a91e8310f282c5a92a89e19921623ef8249711bc0, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))
      state0 := mulmod(scratch0, scratch0, F)
      scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)
      state0 := add(0x1e6ff3216b688c3d996d74367d5cd4c1bc489d46754eb712c243f70d1b53cfbb, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))
      state1 := add(0x01ca8be73832b8d0681487d27d157802d741a6f36cdc2a0576881f9326478875, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))
      state2 := add(0x1f7735706ffe9fc586f976d5bdf223dc680286080b10cea00b9b5de315f9650e, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))
      scratch0 := mulmod(state0, state0, F)
      state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)
      scratch0 := add(0x2522b60f4ea3307640a0c2dce041fba921ac10a3d5f096ef4745ca838285f019, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))
      scratch1 := add(0x23f0bee001b1029d5255075ddc957f833418cad4f52b6c3f8ce16c235572575b, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))
      scratch2 := add(0x2bc1ae8b8ddbb81fcaac2d44555ed5685d142633e9df905f66d9401093082d59, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))
      state0 := mulmod(scratch0, scratch0, F)
      scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)
      state0 := add(0x0f9406b8296564a37304507b8dba3ed162371273a07b1fc98011fcd6ad72205f, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))
      state1 := add(0x2360a8eb0cc7defa67b72998de90714e17e75b174a52ee4acb126c8cd995f0a8, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))
      state2 := add(0x15871a5cddead976804c803cbaef255eb4815a5e96df8b006dcbbc2767f88948, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))
      scratch0 := mulmod(state0, state0, F)
      state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)
      scratch0 := add(0x193a56766998ee9e0a8652dd2f3b1da0362f4f54f72379544f957ccdeefb420f, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))
      scratch1 := add(0x2a394a43934f86982f9be56ff4fab1703b2e63c8ad334834e4309805e777ae0f, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))
      scratch2 := add(0x1859954cfeb8695f3e8b635dcb345192892cd11223443ba7b4166e8876c0d142, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))
      state0 := mulmod(scratch0, scratch0, F)
      scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)
      state0 := add(0x04e1181763050e58013444dbcb99f1902b11bc25d90bbdca408d3819f4fed32b, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))
      state1 := add(0x0fdb253dee83869d40c335ea64de8c5bb10eb82db08b5e8b1f5e5552bfd05f23, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))
      state2 := add(0x058cbe8a9a5027bdaa4efb623adead6275f08686f1c08984a9d7c5bae9b4f1c0, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))
      scratch0 := mulmod(state0, state0, F)
      state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)
      scratch0 := add(0x1382edce9971e186497eadb1aeb1f52b23b4b83bef023ab0d15228b4cceca59a, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))
      scratch1 := add(0x03464990f045c6ee0819ca51fd11b0be7f61b8eb99f14b77e1e6634601d9e8b5, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))
      scratch2 := add(0x23f7bfc8720dc296fff33b41f98ff83c6fcab4605db2eb5aaa5bc137aeb70a58, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))
      state0 := mulmod(scratch0, scratch0, F)
      scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)
      state0 := add(0x0a59a158e3eec2117e6e94e7f0e9decf18c3ffd5e1531a9219636158bbaf62f2, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))
      state1 := add(0x06ec54c80381c052b58bf23b312ffd3ce2c4eba065420af8f4c23ed0075fd07b, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))
      state2 := add(0x118872dc832e0eb5476b56648e867ec8b09340f7a7bcb1b4962f0ff9ed1f9d01, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))
      scratch0 := mulmod(state0, state0, F)
      state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)
      scratch0 := add(0x13d69fa127d834165ad5c7cba7ad59ed52e0b0f0e42d7fea95e1906b520921b1, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))
      scratch1 := add(0x169a177f63ea681270b1c6877a73d21bde143942fb71dc55fd8a49f19f10c77b, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))
      scratch2 := add(0x04ef51591c6ead97ef42f287adce40d93abeb032b922f66ffb7e9a5a7450544d, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))
      state0 := mulmod(scratch0, scratch0, F)
      scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)
      state0 := add(0x256e175a1dc079390ecd7ca703fb2e3b19ec61805d4f03ced5f45ee6dd0f69ec, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))
      state1 := add(0x30102d28636abd5fe5f2af412ff6004f75cc360d3205dd2da002813d3e2ceeb2, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))
      state2 := add(0x10998e42dfcd3bbf1c0714bc73eb1bf40443a3fa99bef4a31fd31be182fcc792, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))
      scratch0 := mulmod(state0, state0, F)
      state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)
      scratch0 := add(0x193edd8e9fcf3d7625fa7d24b598a1d89f3362eaf4d582efecad76f879e36860, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))
      scratch1 := add(0x18168afd34f2d915d0368ce80b7b3347d1c7a561ce611425f2664d7aa51f0b5d, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))
      scratch2 := add(0x29383c01ebd3b6ab0c017656ebe658b6a328ec77bc33626e29e2e95b33ea6111, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))
      state0 := mulmod(scratch0, scratch0, F)
      scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)
      state0 := add(0x10646d2f2603de39a1f4ae5e7771a64a702db6e86fb76ab600bf573f9010c711, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))
      state1 := add(0x0beb5e07d1b27145f575f1395a55bf132f90c25b40da7b3864d0242dcb1117fb, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))
      state2 := add(0x16d685252078c133dc0d3ecad62b5c8830f95bb2e54b59abdffbf018d96fa336, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))
      scratch0 := mulmod(state0, state0, F)
      state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)
      scratch0 := add(0x0a6abd1d833938f33c74154e0404b4b40a555bbbec21ddfafd672dd62047f01a, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))
      scratch1 := add(0x1a679f5d36eb7b5c8ea12a4c2dedc8feb12dffeec450317270a6f19b34cf1860, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))
      scratch2 := add(0x0980fb233bd456c23974d50e0ebfde4726a423eada4e8f6ffbc7592e3f1b93d6, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))
      state0 := mulmod(scratch0, scratch0, F)
      scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)
      state0 := add(0x161b42232e61b84cbf1810af93a38fc0cece3d5628c9282003ebacb5c312c72b, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))
      state1 := add(0x0ada10a90c7f0520950f7d47a60d5e6a493f09787f1564e5d09203db47de1a0b, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))
      state2 := add(0x1a730d372310ba82320345a29ac4238ed3f07a8a2b4e121bb50ddb9af407f451, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))
      scratch0 := mulmod(state0, state0, F)
      state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)
      scratch0 := add(0x2c8120f268ef054f817064c369dda7ea908377feaba5c4dffbda10ef58e8c556, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))
      scratch1 := add(0x1c7c8824f758753fa57c00789c684217b930e95313bcb73e6e7b8649a4968f70, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))
      scratch2 := add(0x2cd9ed31f5f8691c8e39e4077a74faa0f400ad8b491eb3f7b47b27fa3fd1cf77, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))
      state0 := mulmod(scratch0, scratch0, F)
      scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)
      state0 := add(0x23ff4f9d46813457cf60d92f57618399a5e022ac321ca550854ae23918a22eea, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))
      state1 := add(0x09945a5d147a4f66ceece6405dddd9d0af5a2c5103529407dff1ea58f180426d, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))
      state2 := add(0x188d9c528025d4c2b67660c6b771b90f7c7da6eaa29d3f268a6dd223ec6fc630, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))
      scratch0 := mulmod(state0, state0, F)
      state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)
      scratch0 := add(0x3050e37996596b7f81f68311431d8734dba7d926d3633595e0c0d8ddf4f0f47f, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))
      scratch1 := add(0x15af1169396830a91600ca8102c35c426ceae5461e3f95d89d829518d30afd78, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))
      scratch2 := add(0x1da6d09885432ea9a06d9f37f873d985dae933e351466b2904284da3320d8acc, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))
      state0 := mulmod(scratch0, scratch0, F)
      scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)
      state0 := add(0x2796ea90d269af29f5f8acf33921124e4e4fad3dbe658945e546ee411ddaa9cb, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))
      state1 := add(0x202d7dd1da0f6b4b0325c8b3307742f01e15612ec8e9304a7cb0319e01d32d60, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))
      state2 := add(0x096d6790d05bb759156a952ba263d672a2d7f9c788f4c831a29dace4c0f8be5f, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))
      scratch0 := mulmod(state0, state0, F)
      state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)
      scratch0 := add(0x054efa1f65b0fce283808965275d877b438da23ce5b13e1963798cb1447d25a4, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))
      scratch1 := add(0x1b162f83d917e93edb3308c29802deb9d8aa690113b2e14864ccf6e18e4165f1, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))
      scratch2 := add(0x21e5241e12564dd6fd9f1cdd2a0de39eedfefc1466cc568ec5ceb745a0506edc, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))
      state0 := mulmod(scratch0, scratch0, F)
      scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)
      state0 := mulmod(scratch1, scratch1, F)
      scratch1 := mulmod(mulmod(state0, state0, F), scratch1, F)
      state0 := mulmod(scratch2, scratch2, F)
      scratch2 := mulmod(mulmod(state0, state0, F), scratch2, F)
      state0 := add(0x1cfb5662e8cf5ac9226a80ee17b36abecb73ab5f87e161927b4349e10e4bdf08, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))
      state1 := add(0x0f21177e302a771bbae6d8d1ecb373b62c99af346220ac0129c53f666eb24100, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))
      state2 := add(0x1671522374606992affb0dd7f71b12bec4236aede6290546bcef7e1f515c2320, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))
      scratch0 := mulmod(state0, state0, F)
      state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)
      scratch0 := mulmod(state1, state1, F)
      state1 := mulmod(mulmod(scratch0, scratch0, F), state1, F)
      scratch0 := mulmod(state2, state2, F)
      state2 := mulmod(mulmod(scratch0, scratch0, F), state2, F)
      scratch0 := add(0x0fa3ec5b9488259c2eb4cf24501bfad9be2ec9e42c5cc8ccd419d2a692cad870, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))
      scratch1 := add(0x193c0e04e0bd298357cb266c1506080ed36edce85c648cc085e8c57b1ab54bba, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))
      scratch2 := add(0x102adf8ef74735a27e9128306dcbc3c99f6f7291cd406578ce14ea2adaba68f8, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))
      state0 := mulmod(scratch0, scratch0, F)
      scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)
      state0 := mulmod(scratch1, scratch1, F)
      scratch1 := mulmod(mulmod(state0, state0, F), scratch1, F)
      state0 := mulmod(scratch2, scratch2, F)
      scratch2 := mulmod(mulmod(state0, state0, F), scratch2, F)
      state0 := add(0x0fe0af7858e49859e2a54d6f1ad945b1316aa24bfbdd23ae40a6d0cb70c3eab1, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))
      state1 := add(0x216f6717bbc7dedb08536a2220843f4e2da5f1daa9ebdefde8a5ea7344798d22, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))
      state2 := add(0x1da55cc900f0d21f4a3e694391918a1b3c23b2ac773c6b3ef88e2e4228325161, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))
      scratch0 := mulmod(state0, state0, F)
      state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)
      scratch0 := mulmod(state1, state1, F)
      state1 := mulmod(mulmod(scratch0, scratch0, F), state1, F)
      scratch0 := mulmod(state2, state2, F)
      state2 := mulmod(mulmod(scratch0, scratch0, F), state2, F)

      mstore(0x0, mod(add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)), F))

      return(0, 0x20)
    }
  }
}

File 13 of 15 : Constants.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.4;

uint256 constant SNARK_SCALAR_FIELD = 21888242871839275222246405745257275088548364400416034343698204186575808495617;

File 14 of 15 : IERC20.sol
// 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.1.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);
}

Settings
{
  "remappings": [
    "forge-std/=../../node_modules/forge-std/src/",
    "halmos-cheatcodes/=../../node_modules/halmos-cheatcodes/",
    "lean-imt/=../../node_modules/@zk-kit/lean-imt.sol/",
    "@oz/=../../node_modules/@openzeppelin/contracts/",
    "@oz-upgradeable/=../../node_modules/@openzeppelin/contracts-upgradeable/",
    "@upgrades/=../../node_modules/@openzeppelin/foundry-upgrades/src/",
    "poseidon/=../../node_modules/poseidon-solidity/",
    "contracts/=src/contracts/",
    "interfaces/=src/interfaces/",
    "@openzeppelin/=../../node_modules/@openzeppelin/",
    "@zk-kit/=../../node_modules/@zk-kit/",
    "ds-test/=../../node_modules/@openzeppelin/foundry-upgrades/lib/solidity-stringutils/lib/ds-test/",
    "poseidon-solidity/=../../node_modules/poseidon-solidity/",
    "solidity-stringutils/=../../node_modules/@openzeppelin/foundry-upgrades/lib/solidity-stringutils/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 10000
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "none",
    "appendCBOR": false
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "cancun",
  "viaIR": false,
  "libraries": {
    "../../node_modules/poseidon-solidity/PoseidonT3.sol": {
      "PoseidonT3": "0x8ffdA7Ed8B4aD98C2C34f54B76821711F4a6f2C5"
    },
    "../../node_modules/poseidon-solidity/PoseidonT4.sol": {
      "PoseidonT4": "0xe393dd7287Db9982e6f845824854Baf8b8e823AF"
    }
  }
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"_entrypoint","type":"address"},{"internalType":"address","name":"_withdrawalVerifier","type":"address"},{"internalType":"address","name":"_ragequitVerifier","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ContextMismatch","type":"error"},{"inputs":[],"name":"FailedToSendNativeAsset","type":"error"},{"inputs":[],"name":"IncorrectASPRoot","type":"error"},{"inputs":[],"name":"InsufficientValue","type":"error"},{"inputs":[],"name":"InvalidCommitment","type":"error"},{"inputs":[],"name":"InvalidDepositValue","type":"error"},{"inputs":[],"name":"InvalidProcessooor","type":"error"},{"inputs":[],"name":"InvalidProof","type":"error"},{"inputs":[],"name":"InvalidTreeDepth","type":"error"},{"inputs":[],"name":"LeafAlreadyExists","type":"error"},{"inputs":[],"name":"LeafCannotBeZero","type":"error"},{"inputs":[],"name":"LeafGreaterThanSnarkScalarField","type":"error"},{"inputs":[],"name":"MaxTreeDepthReached","type":"error"},{"inputs":[],"name":"NotYetRagequitteable","type":"error"},{"inputs":[],"name":"NullifierAlreadySpent","type":"error"},{"inputs":[],"name":"OnlyEntrypoint","type":"error"},{"inputs":[],"name":"OnlyOriginalDepositor","type":"error"},{"inputs":[],"name":"PoolIsDead","type":"error"},{"inputs":[],"name":"ScopeMismatch","type":"error"},{"inputs":[],"name":"UnknownStateRoot","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_depositor","type":"address"},{"indexed":false,"internalType":"uint256","name":"_commitment","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_label","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_value","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_precommitmentHash","type":"uint256"}],"name":"Deposited","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_index","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_leaf","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_root","type":"uint256"}],"name":"LeafInserted","type":"event"},{"anonymous":false,"inputs":[],"name":"PoolDied","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_ragequitter","type":"address"},{"indexed":false,"internalType":"uint256","name":"_commitment","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_label","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_value","type":"uint256"}],"name":"Ragequit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_processooor","type":"address"},{"indexed":false,"internalType":"uint256","name":"_value","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_spentNullifier","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_newCommitment","type":"uint256"}],"name":"Withdrawn","type":"event"},{"inputs":[],"name":"ASSET","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ENTRYPOINT","outputs":[{"internalType":"contract IEntrypoint","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_TREE_DEPTH","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"RAGEQUIT_VERIFIER","outputs":[{"internalType":"contract IVerifier","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ROOT_HISTORY_SIZE","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SCOPE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"WITHDRAWAL_VERIFIER","outputs":[{"internalType":"contract IVerifier","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"currentRoot","outputs":[{"internalType":"uint256","name":"_root","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"currentRootIndex","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"currentTreeDepth","outputs":[{"internalType":"uint256","name":"_depth","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"currentTreeSize","outputs":[{"internalType":"uint256","name":"_size","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"dead","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_depositor","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"},{"internalType":"uint256","name":"_precommitmentHash","type":"uint256"}],"name":"deposit","outputs":[{"internalType":"uint256","name":"_commitment","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_label","type":"uint256"}],"name":"depositors","outputs":[{"internalType":"address","name":"_depositooor","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nonce","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_nullifierHash","type":"uint256"}],"name":"nullifierHashes","outputs":[{"internalType":"bool","name":"_spent","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint256[2]","name":"pA","type":"uint256[2]"},{"internalType":"uint256[2][2]","name":"pB","type":"uint256[2][2]"},{"internalType":"uint256[2]","name":"pC","type":"uint256[2]"},{"internalType":"uint256[4]","name":"pubSignals","type":"uint256[4]"}],"internalType":"struct ProofLib.RagequitProof","name":"_proof","type":"tuple"}],"name":"ragequit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_index","type":"uint256"}],"name":"roots","outputs":[{"internalType":"uint256","name":"_root","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"windDown","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"processooor","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct IPrivacyPool.Withdrawal","name":"_withdrawal","type":"tuple"},{"components":[{"internalType":"uint256[2]","name":"pA","type":"uint256[2]"},{"internalType":"uint256[2][2]","name":"pB","type":"uint256[2][2]"},{"internalType":"uint256[2]","name":"pC","type":"uint256[2]"},{"internalType":"uint256[8]","name":"pubSignals","type":"uint256[8]"}],"internalType":"struct ProofLib.WithdrawProof","name":"_proof","type":"tuple"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]

0x610120604052348015610010575f5ffd5b50604051611f20380380611f2083398101604081905261002f91610181565b82828273eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee808484846001600160a01b0383166100725760405163d92e233d60e01b815260040160405180910390fd5b6001600160a01b0381166100995760405163d92e233d60e01b815260040160405180910390fd5b6001600160a01b0382166100c05760405163d92e233d60e01b815260040160405180910390fd5b6001600160a01b0384166080526040516001600160601b031930606090811b8216602084015246603484015286901b1660548201527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190606801604051602081830303815290604052805190602001205f1c61013c91906101c1565b60a0526001600160a01b0392831660c05290821660e0521661010052506101e09650505050505050565b80516001600160a01b038116811461017c575f5ffd5b919050565b5f5f5f60608486031215610193575f5ffd5b61019c84610166565b92506101aa60208501610166565b91506101b860408501610166565b90509250925092565b5f826101db57634e487b7160e01b5f52601260045260245ffd5b500690565b60805160a05160c05160e05161010051611cd261024e5f395f81816103a70152610bed01525f81816102c801526109f101525f818161042f0152818161048c015281816109240152610da601525f81816101f10152818161058d01526107f301525f61023d0152611cd25ff3fe608060405260043610610157575f3560e01c8063a8f0f95a116100bb578063da66292511610071578063e4b2fb7911610057578063e4b2fb79146103dd578063e8eb3cc61461041e578063fdab463d14610451575f5ffd5b8063da66292514610396578063e38df5f0146103c9575f5ffd5b8063b85c5145116100a1578063b85c514514610343578063c2b40ae414610357578063cd87a3b414610382575f5ffd5b8063a8f0f95a1461031b578063affed0e01461032f575f5ffd5b80634800d97f1161011057806371235b34116100f657806371235b34146102985780638735eb2b146102b757806390eeb02b146102ea575f5ffd5b80634800d97f1461022c5780634a2b75c814610284575f5ffd5b806330c0766d1161014057806330c0766d146101bf57806333d09200146101e057806336cf7c8714610213575f5ffd5b80630efe6a8b1461015b5780631f79a1e914610181575b5f5ffd5b61016e610169366004611453565b610473565b6040519081526020015b60405180910390f35b34801561018c575f5ffd5b506101af61019b366004611483565b60086020525f908152604090205460ff1681565b6040519015158152602001610178565b3480156101ca575f5ffd5b506101de6101d93660046116bc565b610765565b005b3480156101eb575f5ffd5b5061016e7f000000000000000000000000000000000000000000000000000000000000000081565b34801561021e575f5ffd5b506001546101af9060ff1681565b348015610237575f5ffd5b5061025f7f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610178565b34801561028f575f5ffd5b5060055461016e565b3480156102a3575f5ffd5b506101de6102b23660046117c9565b610b82565b3480156102c2575f5ffd5b5061025f7f000000000000000000000000000000000000000000000000000000000000000081565b3480156102f5575f5ffd5b506003546103069063ffffffff1681565b60405163ffffffff9091168152602001610178565b348015610326575f5ffd5b5060045461016e565b34801561033a575f5ffd5b5061016e5f5481565b34801561034e575f5ffd5b506101de610d8e565b348015610362575f5ffd5b5061016e610371366004611483565b60026020525f908152604090205481565b34801561038d575f5ffd5b50610306604081565b3480156103a1575f5ffd5b5061025f7f000000000000000000000000000000000000000000000000000000000000000081565b3480156103d4575f5ffd5b50610306602081565b3480156103e8575f5ffd5b5061025f6103f7366004611483565b60096020525f908152604090205473ffffffffffffffffffffffffffffffffffffffff1681565b348015610429575f5ffd5b5061025f7f000000000000000000000000000000000000000000000000000000000000000081565b34801561045c575f5ffd5b506005545f9081526006602052604090205461016e565b5f3373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016146104e3576040517f8247bd8000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60015460ff1615610520576040517f924b372700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6fffffffffffffffffffffffffffffffff8310610569576040517f428243e200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f00000000000000000000000000000000000000000000000000000000000000005f5f81546105b9906118a4565b9182905550604080516020810193909352820152606001604051602081830303815290604052805190602001205f1c6105f29190611908565b5f8181526009602090815260409182902080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff8a16179055815160608101835287815290810183905280820186905290517f20cf0a3700000000000000000000000000000000000000000000000000000000815291925073e393dd7287db9982e6f845824854baf8b8e823af916320cf0a37916106a89160040161191b565b602060405180830381865af41580156106c3573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106e7919061194b565b91506106f282610e8e565b506106fd3385610f8d565b60408051838152602081018390529081018590526060810184905273ffffffffffffffffffffffffffffffffffffffff8616907fe3b53cd1a44fbf11535e145d80b8ef1ed6d57a73bf5daa7e939b6b01657d65499060800160405180910390a2509392505050565b8181815f015173ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146107cf576040517f1a7c48e500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001827f0000000000000000000000000000000000000000000000000000000000000000604051602001610823929190611962565b604051602081830303815290604052805190602001205f1c6108459190611908565b61084e82610fca565b14610885576040517fef6daeb000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b602061089082610fdf565b11806108a4575060206108a282610fed565b115b156108db576040517fc21cc8e200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6108ec6108e782610ffb565b611009565b610922576040517ffd3d3c4c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663d7b0fef16040518163ffffffff1660e01b8152600401602060405180830381865afa15801561098b573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906109af919061194b565b6109b88261108c565b146109ef576040517fa6a7824400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663c9219a7a845f01518560200151866040015187606001516040518563ffffffff1660e01b8152600401610a5d9493929190611a3b565b6020604051808303815f875af1158015610a79573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610a9d9190611a97565b610ad3576040517f09bde33900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610ae4610adf8461109a565b6110a8565b610af5610af084611128565b610e8e565b508351610b0a90610b0585611135565b611143565b835173ffffffffffffffffffffffffffffffffffffffff167f75e161b3e824b114fc1a33274bd7091918dd4e639cede50b78b15a4eea956a21610b4c85611135565b610b558661109a565b610b5e87611128565b6040805193845260208401929092529082015260600160405180910390a250505050565b5f610b8c82610ffb565b5f8181526009602052604090205490915073ffffffffffffffffffffffffffffffffffffffff163314610beb576040517f0fb8e27800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16635fe8c13b835f01518460200151856040015186606001516040518563ffffffff1660e01b8152600401610c599493929190611abd565b6020604051808303815f875af1158015610c75573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c999190611a97565b610ccf576040517f09bde33900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610ce0610cdb83611128565b6111de565b610d16576040517fc06789fa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610d22610adf8361109a565b610d2f33610b0584611135565b337fd2b3e868ae101106371f2bd93abc8d5a4de488b9fe47ed122c23625aa7172f13610d5a84611128565b610d6385610ffb565b610d6c86611135565b6040805193845260208401929092529082015260600160405180910390a25050565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610dfd576040517f8247bd8000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60015460ff1615610e3a576040517f924b372700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016811790556040517ff9dc79d7254099fdf1f86b15c91deac3bf8883e4f340ebef6e3c6b870cc315b3905f90a1565b5f610e9a6004836111f5565b60055490915060201015610eda576040517f221f404100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6003545f90604090610ef39063ffffffff166001611b0d565b610efd9190611b29565b63ffffffff81165f81815260026020908152604091829020869055600380547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000016909317909255600454815190815291820186905281018490529091507fcb249c8292372bd11f567786635483fca9e635030baafca55ff1a8940141d2219060600160405180910390a150919050565b803414610fc6576040517f1101129400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050565b60608101515f9060075b602002015192915050565b60608101515f906004610fd4565b60608101515f906006610fd4565b60608101515f906003610fd4565b5f815f0361101857505f919050565b60035463ffffffff165f5b604063ffffffff821610156110835763ffffffff82165f908152600260205260409020548403611057575060019392505050565b604060016110658285611b0d565b61106f9190611b50565b6110799190611b29565b9150600101611023565b505f9392505050565b60608101515f906005610fd4565b60608101515f906001610fd4565b5f8181526008602052604090205460ff16156110f0576040517fb115d85700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f90815260086020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055565b60608101515f9081610fd4565b60608101515f906002610fd4565b5f8273ffffffffffffffffffffffffffffffffffffffff16826040515f6040518083038185875af1925050503d805f8114611199576040519150601f19603f3d011682016040523d82523d5f602084013e61119e565b606091505b50509050806111d9576040517fd58db96200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050565b5f8181526007602052604081205415155b92915050565b5f7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001821061124f576040517fc380a82e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b815f03611288576040517f29691be200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f828152600384016020526040902054156112cf576040517f258a195a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8254600180850154906112e3908390611b6c565b6112ee826002611ca0565b1015611300576112fd816118a4565b90505b60018501819055835f5b828110156113f1578084901c6001166001036113d6576040805180820182525f83815260028a01602090815290839020548252810184905290517f561558fe000000000000000000000000000000000000000000000000000000008152738ffda7ed8b4ad98c2c34f54b76821711f4a6f2c59163561558fe916113909190600401611cab565b602060405180830381865af41580156113ab573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906113cf919061194b565b91506113e9565b5f81815260028801602052604090208290555b60010161130a565b506113fb836118a4565b8087555f928352600287016020908152604080852084905596845260039097019096529390209390935550919050565b803573ffffffffffffffffffffffffffffffffffffffff8116811461144e575f5ffd5b919050565b5f5f5f60608486031215611465575f5ffd5b61146e8461142b565b95602085013595506040909401359392505050565b5f60208284031215611493575f5ffd5b5035919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6040516080810167ffffffffffffffff811182821017156114ea576114ea61149a565b60405290565b6040805190810167ffffffffffffffff811182821017156114ea576114ea61149a565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff8111828210171561155a5761155a61149a565b604052919050565b5f82601f830112611571575f5ffd5b5f61157c6040611513565b905080604084018581111561158f575f5ffd5b845b818110156115a9578035835260209283019201611591565b509195945050505050565b5f82601f8301126115c3575f5ffd5b60406115ce81611513565b8060808501868111156115df575f5ffd5b855b81811015611602576115f38882611562565b845260209093019284016115e1565b50909695505050505050565b5f610200828403121561161f575f5ffd5b6116276114c7565b90506116338383611562565b815261164283604084016115b4565b60208201526116548360c08401611562565b60408201525f8361011f840112611669575f5ffd5b505f8061010061167881611513565b915081925061020085018681111561168e575f5ffd5b908501905b808210156116ae578135845260209384019390910190611693565b505060608301525092915050565b5f5f61022083850312156116ce575f5ffd5b823567ffffffffffffffff8111156116e4575f5ffd5b8301604081860312156116f5575f5ffd5b6116fd6114f0565b6117068261142b565b8152602082013567ffffffffffffffff811115611721575f5ffd5b80830192505085601f830112611735575f5ffd5b813567ffffffffffffffff81111561174f5761174f61149a565b61178060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601611513565b818152876020838601011115611794575f5ffd5b816020850160208301375f60208383010152806020840152505080935050506117c0846020850161160e565b90509250929050565b5f6101808284031280156117db575f5ffd5b505f806117e66114c7565b91506117f28585611562565b825261180185604086016115b4565b60208301526118138560c08601611562565b60408301528461011f850112611827575f5ffd5b505f806118346080611513565b905080915061018085018681111561184a575f5ffd5b61010086015b81811015611868578035845260209384019301611850565b50506060830152509392505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036118d4576118d4611877565b5060010190565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b5f82611916576119166118db565b500690565b6060810181835f5b6003811015611942578151835260209283019290910190600101611923565b50505092915050565b5f6020828403121561195b575f5ffd5b5051919050565b6040815273ffffffffffffffffffffffffffffffffffffffff83511660408201525f6020840151604060608401528051806080850152806020830160a086015e5f60a0828601015260a07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f830116850101925050508260208301529392505050565b805f5b6002811015611a085781518452602093840193909101906001016119e9565b50505050565b805f5b6002811015611a0857611a258483516119e6565b6040939093019260209190910190600101611a11565b6102008101611a4a82876119e6565b611a576040830186611a0e565b611a6460c08301856119e6565b6101008201835f5b6008811015611a8b578151835260209283019290910190600101611a6c565b50505095945050505050565b5f60208284031215611aa7575f5ffd5b81518015158114611ab6575f5ffd5b9392505050565b6101808101611acc82876119e6565b611ad96040830186611a0e565b611ae660c08301856119e6565b6101008201835f5b6004811015611a8b578151835260209283019290910190600101611aee565b63ffffffff81811683821601908111156111ef576111ef611877565b5f63ffffffff831680611b3e57611b3e6118db565b8063ffffffff84160691505092915050565b63ffffffff82811682821603908111156111ef576111ef611877565b808201808211156111ef576111ef611877565b6001815b6001841115611bba57808504811115611b9e57611b9e611877565b6001841615611bac57908102905b60019390931c928002611b83565b935093915050565b5f82611bd0575060016111ef565b81611bdc57505f6111ef565b8160018114611bf25760028114611bfc57611c18565b60019150506111ef565b60ff841115611c0d57611c0d611877565b50506001821b6111ef565b5060208310610133831016604e8410600b8410161715611c3b575081810a6111ef565b611c667fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8484611b7f565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04821115611c9857611c98611877565b029392505050565b5f611ab68383611bc2565b6040810181835f5b6002811015611942578151835260209283019290910190600101611cb3560000000000000000000000006818809eefce719e480a7526d76bd3e561526b46000000000000000000000000022891f938ae7fdc8ab9ead0fbf50aba8c897d6d000000000000000000000000a45aca8604a73d80c551faad6355a5c3a5565ec6

Deployed Bytecode

0x608060405260043610610157575f3560e01c8063a8f0f95a116100bb578063da66292511610071578063e4b2fb7911610057578063e4b2fb79146103dd578063e8eb3cc61461041e578063fdab463d14610451575f5ffd5b8063da66292514610396578063e38df5f0146103c9575f5ffd5b8063b85c5145116100a1578063b85c514514610343578063c2b40ae414610357578063cd87a3b414610382575f5ffd5b8063a8f0f95a1461031b578063affed0e01461032f575f5ffd5b80634800d97f1161011057806371235b34116100f657806371235b34146102985780638735eb2b146102b757806390eeb02b146102ea575f5ffd5b80634800d97f1461022c5780634a2b75c814610284575f5ffd5b806330c0766d1161014057806330c0766d146101bf57806333d09200146101e057806336cf7c8714610213575f5ffd5b80630efe6a8b1461015b5780631f79a1e914610181575b5f5ffd5b61016e610169366004611453565b610473565b6040519081526020015b60405180910390f35b34801561018c575f5ffd5b506101af61019b366004611483565b60086020525f908152604090205460ff1681565b6040519015158152602001610178565b3480156101ca575f5ffd5b506101de6101d93660046116bc565b610765565b005b3480156101eb575f5ffd5b5061016e7f0adeaec0e1538a417bf70421cc25c90da5269c128097beb58d8cbc22dadfae4381565b34801561021e575f5ffd5b506001546101af9060ff1681565b348015610237575f5ffd5b5061025f7f000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee81565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610178565b34801561028f575f5ffd5b5060055461016e565b3480156102a3575f5ffd5b506101de6102b23660046117c9565b610b82565b3480156102c2575f5ffd5b5061025f7f000000000000000000000000022891f938ae7fdc8ab9ead0fbf50aba8c897d6d81565b3480156102f5575f5ffd5b506003546103069063ffffffff1681565b60405163ffffffff9091168152602001610178565b348015610326575f5ffd5b5060045461016e565b34801561033a575f5ffd5b5061016e5f5481565b34801561034e575f5ffd5b506101de610d8e565b348015610362575f5ffd5b5061016e610371366004611483565b60026020525f908152604090205481565b34801561038d575f5ffd5b50610306604081565b3480156103a1575f5ffd5b5061025f7f000000000000000000000000a45aca8604a73d80c551faad6355a5c3a5565ec681565b3480156103d4575f5ffd5b50610306602081565b3480156103e8575f5ffd5b5061025f6103f7366004611483565b60096020525f908152604090205473ffffffffffffffffffffffffffffffffffffffff1681565b348015610429575f5ffd5b5061025f7f0000000000000000000000006818809eefce719e480a7526d76bd3e561526b4681565b34801561045c575f5ffd5b506005545f9081526006602052604090205461016e565b5f3373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000006818809eefce719e480a7526d76bd3e561526b4616146104e3576040517f8247bd8000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60015460ff1615610520576040517f924b372700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6fffffffffffffffffffffffffffffffff8310610569576040517f428243e200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f0adeaec0e1538a417bf70421cc25c90da5269c128097beb58d8cbc22dadfae435f5f81546105b9906118a4565b9182905550604080516020810193909352820152606001604051602081830303815290604052805190602001205f1c6105f29190611908565b5f8181526009602090815260409182902080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff8a16179055815160608101835287815290810183905280820186905290517f20cf0a3700000000000000000000000000000000000000000000000000000000815291925073e393dd7287db9982e6f845824854baf8b8e823af916320cf0a37916106a89160040161191b565b602060405180830381865af41580156106c3573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106e7919061194b565b91506106f282610e8e565b506106fd3385610f8d565b60408051838152602081018390529081018590526060810184905273ffffffffffffffffffffffffffffffffffffffff8616907fe3b53cd1a44fbf11535e145d80b8ef1ed6d57a73bf5daa7e939b6b01657d65499060800160405180910390a2509392505050565b8181815f015173ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146107cf576040517f1a7c48e500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001827f0adeaec0e1538a417bf70421cc25c90da5269c128097beb58d8cbc22dadfae43604051602001610823929190611962565b604051602081830303815290604052805190602001205f1c6108459190611908565b61084e82610fca565b14610885576040517fef6daeb000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b602061089082610fdf565b11806108a4575060206108a282610fed565b115b156108db576040517fc21cc8e200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6108ec6108e782610ffb565b611009565b610922576040517ffd3d3c4c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f0000000000000000000000006818809eefce719e480a7526d76bd3e561526b4673ffffffffffffffffffffffffffffffffffffffff1663d7b0fef16040518163ffffffff1660e01b8152600401602060405180830381865afa15801561098b573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906109af919061194b565b6109b88261108c565b146109ef576040517fa6a7824400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f000000000000000000000000022891f938ae7fdc8ab9ead0fbf50aba8c897d6d73ffffffffffffffffffffffffffffffffffffffff1663c9219a7a845f01518560200151866040015187606001516040518563ffffffff1660e01b8152600401610a5d9493929190611a3b565b6020604051808303815f875af1158015610a79573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610a9d9190611a97565b610ad3576040517f09bde33900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610ae4610adf8461109a565b6110a8565b610af5610af084611128565b610e8e565b508351610b0a90610b0585611135565b611143565b835173ffffffffffffffffffffffffffffffffffffffff167f75e161b3e824b114fc1a33274bd7091918dd4e639cede50b78b15a4eea956a21610b4c85611135565b610b558661109a565b610b5e87611128565b6040805193845260208401929092529082015260600160405180910390a250505050565b5f610b8c82610ffb565b5f8181526009602052604090205490915073ffffffffffffffffffffffffffffffffffffffff163314610beb576040517f0fb8e27800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f000000000000000000000000a45aca8604a73d80c551faad6355a5c3a5565ec673ffffffffffffffffffffffffffffffffffffffff16635fe8c13b835f01518460200151856040015186606001516040518563ffffffff1660e01b8152600401610c599493929190611abd565b6020604051808303815f875af1158015610c75573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c999190611a97565b610ccf576040517f09bde33900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610ce0610cdb83611128565b6111de565b610d16576040517fc06789fa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610d22610adf8361109a565b610d2f33610b0584611135565b337fd2b3e868ae101106371f2bd93abc8d5a4de488b9fe47ed122c23625aa7172f13610d5a84611128565b610d6385610ffb565b610d6c86611135565b6040805193845260208401929092529082015260600160405180910390a25050565b3373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000006818809eefce719e480a7526d76bd3e561526b461614610dfd576040517f8247bd8000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60015460ff1615610e3a576040517f924b372700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016811790556040517ff9dc79d7254099fdf1f86b15c91deac3bf8883e4f340ebef6e3c6b870cc315b3905f90a1565b5f610e9a6004836111f5565b60055490915060201015610eda576040517f221f404100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6003545f90604090610ef39063ffffffff166001611b0d565b610efd9190611b29565b63ffffffff81165f81815260026020908152604091829020869055600380547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000016909317909255600454815190815291820186905281018490529091507fcb249c8292372bd11f567786635483fca9e635030baafca55ff1a8940141d2219060600160405180910390a150919050565b803414610fc6576040517f1101129400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050565b60608101515f9060075b602002015192915050565b60608101515f906004610fd4565b60608101515f906006610fd4565b60608101515f906003610fd4565b5f815f0361101857505f919050565b60035463ffffffff165f5b604063ffffffff821610156110835763ffffffff82165f908152600260205260409020548403611057575060019392505050565b604060016110658285611b0d565b61106f9190611b50565b6110799190611b29565b9150600101611023565b505f9392505050565b60608101515f906005610fd4565b60608101515f906001610fd4565b5f8181526008602052604090205460ff16156110f0576040517fb115d85700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f90815260086020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055565b60608101515f9081610fd4565b60608101515f906002610fd4565b5f8273ffffffffffffffffffffffffffffffffffffffff16826040515f6040518083038185875af1925050503d805f8114611199576040519150601f19603f3d011682016040523d82523d5f602084013e61119e565b606091505b50509050806111d9576040517fd58db96200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050565b5f8181526007602052604081205415155b92915050565b5f7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001821061124f576040517fc380a82e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b815f03611288576040517f29691be200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f828152600384016020526040902054156112cf576040517f258a195a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8254600180850154906112e3908390611b6c565b6112ee826002611ca0565b1015611300576112fd816118a4565b90505b60018501819055835f5b828110156113f1578084901c6001166001036113d6576040805180820182525f83815260028a01602090815290839020548252810184905290517f561558fe000000000000000000000000000000000000000000000000000000008152738ffda7ed8b4ad98c2c34f54b76821711f4a6f2c59163561558fe916113909190600401611cab565b602060405180830381865af41580156113ab573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906113cf919061194b565b91506113e9565b5f81815260028801602052604090208290555b60010161130a565b506113fb836118a4565b8087555f928352600287016020908152604080852084905596845260039097019096529390209390935550919050565b803573ffffffffffffffffffffffffffffffffffffffff8116811461144e575f5ffd5b919050565b5f5f5f60608486031215611465575f5ffd5b61146e8461142b565b95602085013595506040909401359392505050565b5f60208284031215611493575f5ffd5b5035919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6040516080810167ffffffffffffffff811182821017156114ea576114ea61149a565b60405290565b6040805190810167ffffffffffffffff811182821017156114ea576114ea61149a565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff8111828210171561155a5761155a61149a565b604052919050565b5f82601f830112611571575f5ffd5b5f61157c6040611513565b905080604084018581111561158f575f5ffd5b845b818110156115a9578035835260209283019201611591565b509195945050505050565b5f82601f8301126115c3575f5ffd5b60406115ce81611513565b8060808501868111156115df575f5ffd5b855b81811015611602576115f38882611562565b845260209093019284016115e1565b50909695505050505050565b5f610200828403121561161f575f5ffd5b6116276114c7565b90506116338383611562565b815261164283604084016115b4565b60208201526116548360c08401611562565b60408201525f8361011f840112611669575f5ffd5b505f8061010061167881611513565b915081925061020085018681111561168e575f5ffd5b908501905b808210156116ae578135845260209384019390910190611693565b505060608301525092915050565b5f5f61022083850312156116ce575f5ffd5b823567ffffffffffffffff8111156116e4575f5ffd5b8301604081860312156116f5575f5ffd5b6116fd6114f0565b6117068261142b565b8152602082013567ffffffffffffffff811115611721575f5ffd5b80830192505085601f830112611735575f5ffd5b813567ffffffffffffffff81111561174f5761174f61149a565b61178060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601611513565b818152876020838601011115611794575f5ffd5b816020850160208301375f60208383010152806020840152505080935050506117c0846020850161160e565b90509250929050565b5f6101808284031280156117db575f5ffd5b505f806117e66114c7565b91506117f28585611562565b825261180185604086016115b4565b60208301526118138560c08601611562565b60408301528461011f850112611827575f5ffd5b505f806118346080611513565b905080915061018085018681111561184a575f5ffd5b61010086015b81811015611868578035845260209384019301611850565b50506060830152509392505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036118d4576118d4611877565b5060010190565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b5f82611916576119166118db565b500690565b6060810181835f5b6003811015611942578151835260209283019290910190600101611923565b50505092915050565b5f6020828403121561195b575f5ffd5b5051919050565b6040815273ffffffffffffffffffffffffffffffffffffffff83511660408201525f6020840151604060608401528051806080850152806020830160a086015e5f60a0828601015260a07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f830116850101925050508260208301529392505050565b805f5b6002811015611a085781518452602093840193909101906001016119e9565b50505050565b805f5b6002811015611a0857611a258483516119e6565b6040939093019260209190910190600101611a11565b6102008101611a4a82876119e6565b611a576040830186611a0e565b611a6460c08301856119e6565b6101008201835f5b6008811015611a8b578151835260209283019290910190600101611a6c565b50505095945050505050565b5f60208284031215611aa7575f5ffd5b81518015158114611ab6575f5ffd5b9392505050565b6101808101611acc82876119e6565b611ad96040830186611a0e565b611ae660c08301856119e6565b6101008201835f5b6004811015611a8b578151835260209283019290910190600101611aee565b63ffffffff81811683821601908111156111ef576111ef611877565b5f63ffffffff831680611b3e57611b3e6118db565b8063ffffffff84160691505092915050565b63ffffffff82811682821603908111156111ef576111ef611877565b808201808211156111ef576111ef611877565b6001815b6001841115611bba57808504811115611b9e57611b9e611877565b6001841615611bac57908102905b60019390931c928002611b83565b935093915050565b5f82611bd0575060016111ef565b81611bdc57505f6111ef565b8160018114611bf25760028114611bfc57611c18565b60019150506111ef565b60ff841115611c0d57611c0d611877565b50506001821b6111ef565b5060208310610133831016604e8410600b8410161715611c3b575081810a6111ef565b611c667fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8484611b7f565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04821115611c9857611c98611877565b029392505050565b5f611ab68383611bc2565b6040810181835f5b6002811015611942578151835260209283019290910190600101611cb356

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading
Loading...
Loading

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.