ETH Price: $2,213.99 (-1.38%)

Contract

0x3D665d334E99cc6Fee0Ffc505FC7Fc1C1dD3515f

Overview

ETH Balance

0 ETH

ETH Value

$0.00

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Block
From
To

There are no matching entries

1 Internal Transaction found.

Latest 1 internal transaction

Advanced mode:
Parent Transaction Hash Block From To
423184242026-03-09 14:33:0333 days ago1773066783  Contract Creation0 ETH

Cross-Chain Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
SmartPool

Compiler Version
v0.8.28+commit.7893614a

Optimization Enabled:
Yes with 200 runs

Other Settings:
cancun EvmVersion
File 1 of 48 : SmartPool.sol
// SPDX-License-Identifier: Apache-2.0-or-later
pragma solidity 0.8.28;

import {ISmartPool} from "./ISmartPool.sol";
import {MixinImmutables} from "./core/immutable/MixinImmutables.sol";
import {MixinStorage} from "./core/immutable/MixinStorage.sol";
import {MixinPoolState} from "./core/state/MixinPoolState.sol";
import {MixinStorageAccessible} from "./core/state/MixinStorageAccessible.sol";
import {MixinAbstract} from "./core/sys/MixinAbstract.sol";
import {MixinInitializer} from "./core/sys/MixinInitializer.sol";
import {MixinFallback} from "./core/sys/MixinFallback.sol";

/// @title ISmartPool - A set of rules for Rigoblock pools.
/// @author Gabriele Rigo - <[email protected]>
// solhint-disable-next-line
contract SmartPool is
    ISmartPool,
    MixinStorage,
    MixinFallback,
    MixinInitializer,
    MixinAbstract,
    MixinPoolState,
    MixinStorageAccessible
{
    /// @notice Owner is initialized to 0 to lock owner actions in this implementation.
    /// @notice Kyc provider set as will effectively lock direct mint/burn actions.
    /// @notice ExtensionsMap validation is performed in MixinImmutables constructor.
    constructor(
        address authority,
        address extensionsMap,
        address tokenJar
    ) MixinImmutables(authority, extensionsMap, tokenJar) {
        // we lock implementation at deploy
        pool().owner = _ZERO_ADDRESS;
        poolParams().kycProvider = _BASE_TOKEN_FLAG;
    }
}

File 2 of 48 : SafeCast.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SafeCast.sol)
// This file was procedurally generated from scripts/generate/templates/SafeCast.js.

pragma solidity ^0.8.20;

/**
 * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
 * checks.
 *
 * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
 * easily result in undesired exploitation or bugs, since developers usually
 * assume that overflows raise errors. `SafeCast` restores this intuition by
 * reverting the transaction when such an operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 */
library SafeCast {
    /**
     * @dev Value doesn't fit in an uint of `bits` size.
     */
    error SafeCastOverflowedUintDowncast(uint8 bits, uint256 value);

    /**
     * @dev An int value doesn't fit in an uint of `bits` size.
     */
    error SafeCastOverflowedIntToUint(int256 value);

    /**
     * @dev Value doesn't fit in an int of `bits` size.
     */
    error SafeCastOverflowedIntDowncast(uint8 bits, int256 value);

    /**
     * @dev An uint value doesn't fit in an int of `bits` size.
     */
    error SafeCastOverflowedUintToInt(uint256 value);

    /**
     * @dev Returns the downcasted uint248 from uint256, reverting on
     * overflow (when the input is greater than largest uint248).
     *
     * Counterpart to Solidity's `uint248` operator.
     *
     * Requirements:
     *
     * - input must fit into 248 bits
     */
    function toUint248(uint256 value) internal pure returns (uint248) {
        if (value > type(uint248).max) {
            revert SafeCastOverflowedUintDowncast(248, value);
        }
        return uint248(value);
    }

    /**
     * @dev Returns the downcasted uint240 from uint256, reverting on
     * overflow (when the input is greater than largest uint240).
     *
     * Counterpart to Solidity's `uint240` operator.
     *
     * Requirements:
     *
     * - input must fit into 240 bits
     */
    function toUint240(uint256 value) internal pure returns (uint240) {
        if (value > type(uint240).max) {
            revert SafeCastOverflowedUintDowncast(240, value);
        }
        return uint240(value);
    }

    /**
     * @dev Returns the downcasted uint232 from uint256, reverting on
     * overflow (when the input is greater than largest uint232).
     *
     * Counterpart to Solidity's `uint232` operator.
     *
     * Requirements:
     *
     * - input must fit into 232 bits
     */
    function toUint232(uint256 value) internal pure returns (uint232) {
        if (value > type(uint232).max) {
            revert SafeCastOverflowedUintDowncast(232, value);
        }
        return uint232(value);
    }

    /**
     * @dev Returns the downcasted uint224 from uint256, reverting on
     * overflow (when the input is greater than largest uint224).
     *
     * Counterpart to Solidity's `uint224` operator.
     *
     * Requirements:
     *
     * - input must fit into 224 bits
     */
    function toUint224(uint256 value) internal pure returns (uint224) {
        if (value > type(uint224).max) {
            revert SafeCastOverflowedUintDowncast(224, value);
        }
        return uint224(value);
    }

    /**
     * @dev Returns the downcasted uint216 from uint256, reverting on
     * overflow (when the input is greater than largest uint216).
     *
     * Counterpart to Solidity's `uint216` operator.
     *
     * Requirements:
     *
     * - input must fit into 216 bits
     */
    function toUint216(uint256 value) internal pure returns (uint216) {
        if (value > type(uint216).max) {
            revert SafeCastOverflowedUintDowncast(216, value);
        }
        return uint216(value);
    }

    /**
     * @dev Returns the downcasted uint208 from uint256, reverting on
     * overflow (when the input is greater than largest uint208).
     *
     * Counterpart to Solidity's `uint208` operator.
     *
     * Requirements:
     *
     * - input must fit into 208 bits
     */
    function toUint208(uint256 value) internal pure returns (uint208) {
        if (value > type(uint208).max) {
            revert SafeCastOverflowedUintDowncast(208, value);
        }
        return uint208(value);
    }

    /**
     * @dev Returns the downcasted uint200 from uint256, reverting on
     * overflow (when the input is greater than largest uint200).
     *
     * Counterpart to Solidity's `uint200` operator.
     *
     * Requirements:
     *
     * - input must fit into 200 bits
     */
    function toUint200(uint256 value) internal pure returns (uint200) {
        if (value > type(uint200).max) {
            revert SafeCastOverflowedUintDowncast(200, value);
        }
        return uint200(value);
    }

    /**
     * @dev Returns the downcasted uint192 from uint256, reverting on
     * overflow (when the input is greater than largest uint192).
     *
     * Counterpart to Solidity's `uint192` operator.
     *
     * Requirements:
     *
     * - input must fit into 192 bits
     */
    function toUint192(uint256 value) internal pure returns (uint192) {
        if (value > type(uint192).max) {
            revert SafeCastOverflowedUintDowncast(192, value);
        }
        return uint192(value);
    }

    /**
     * @dev Returns the downcasted uint184 from uint256, reverting on
     * overflow (when the input is greater than largest uint184).
     *
     * Counterpart to Solidity's `uint184` operator.
     *
     * Requirements:
     *
     * - input must fit into 184 bits
     */
    function toUint184(uint256 value) internal pure returns (uint184) {
        if (value > type(uint184).max) {
            revert SafeCastOverflowedUintDowncast(184, value);
        }
        return uint184(value);
    }

    /**
     * @dev Returns the downcasted uint176 from uint256, reverting on
     * overflow (when the input is greater than largest uint176).
     *
     * Counterpart to Solidity's `uint176` operator.
     *
     * Requirements:
     *
     * - input must fit into 176 bits
     */
    function toUint176(uint256 value) internal pure returns (uint176) {
        if (value > type(uint176).max) {
            revert SafeCastOverflowedUintDowncast(176, value);
        }
        return uint176(value);
    }

    /**
     * @dev Returns the downcasted uint168 from uint256, reverting on
     * overflow (when the input is greater than largest uint168).
     *
     * Counterpart to Solidity's `uint168` operator.
     *
     * Requirements:
     *
     * - input must fit into 168 bits
     */
    function toUint168(uint256 value) internal pure returns (uint168) {
        if (value > type(uint168).max) {
            revert SafeCastOverflowedUintDowncast(168, value);
        }
        return uint168(value);
    }

    /**
     * @dev Returns the downcasted uint160 from uint256, reverting on
     * overflow (when the input is greater than largest uint160).
     *
     * Counterpart to Solidity's `uint160` operator.
     *
     * Requirements:
     *
     * - input must fit into 160 bits
     */
    function toUint160(uint256 value) internal pure returns (uint160) {
        if (value > type(uint160).max) {
            revert SafeCastOverflowedUintDowncast(160, value);
        }
        return uint160(value);
    }

    /**
     * @dev Returns the downcasted uint152 from uint256, reverting on
     * overflow (when the input is greater than largest uint152).
     *
     * Counterpart to Solidity's `uint152` operator.
     *
     * Requirements:
     *
     * - input must fit into 152 bits
     */
    function toUint152(uint256 value) internal pure returns (uint152) {
        if (value > type(uint152).max) {
            revert SafeCastOverflowedUintDowncast(152, value);
        }
        return uint152(value);
    }

    /**
     * @dev Returns the downcasted uint144 from uint256, reverting on
     * overflow (when the input is greater than largest uint144).
     *
     * Counterpart to Solidity's `uint144` operator.
     *
     * Requirements:
     *
     * - input must fit into 144 bits
     */
    function toUint144(uint256 value) internal pure returns (uint144) {
        if (value > type(uint144).max) {
            revert SafeCastOverflowedUintDowncast(144, value);
        }
        return uint144(value);
    }

    /**
     * @dev Returns the downcasted uint136 from uint256, reverting on
     * overflow (when the input is greater than largest uint136).
     *
     * Counterpart to Solidity's `uint136` operator.
     *
     * Requirements:
     *
     * - input must fit into 136 bits
     */
    function toUint136(uint256 value) internal pure returns (uint136) {
        if (value > type(uint136).max) {
            revert SafeCastOverflowedUintDowncast(136, value);
        }
        return uint136(value);
    }

    /**
     * @dev Returns the downcasted uint128 from uint256, reverting on
     * overflow (when the input is greater than largest uint128).
     *
     * Counterpart to Solidity's `uint128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     */
    function toUint128(uint256 value) internal pure returns (uint128) {
        if (value > type(uint128).max) {
            revert SafeCastOverflowedUintDowncast(128, value);
        }
        return uint128(value);
    }

    /**
     * @dev Returns the downcasted uint120 from uint256, reverting on
     * overflow (when the input is greater than largest uint120).
     *
     * Counterpart to Solidity's `uint120` operator.
     *
     * Requirements:
     *
     * - input must fit into 120 bits
     */
    function toUint120(uint256 value) internal pure returns (uint120) {
        if (value > type(uint120).max) {
            revert SafeCastOverflowedUintDowncast(120, value);
        }
        return uint120(value);
    }

    /**
     * @dev Returns the downcasted uint112 from uint256, reverting on
     * overflow (when the input is greater than largest uint112).
     *
     * Counterpart to Solidity's `uint112` operator.
     *
     * Requirements:
     *
     * - input must fit into 112 bits
     */
    function toUint112(uint256 value) internal pure returns (uint112) {
        if (value > type(uint112).max) {
            revert SafeCastOverflowedUintDowncast(112, value);
        }
        return uint112(value);
    }

    /**
     * @dev Returns the downcasted uint104 from uint256, reverting on
     * overflow (when the input is greater than largest uint104).
     *
     * Counterpart to Solidity's `uint104` operator.
     *
     * Requirements:
     *
     * - input must fit into 104 bits
     */
    function toUint104(uint256 value) internal pure returns (uint104) {
        if (value > type(uint104).max) {
            revert SafeCastOverflowedUintDowncast(104, value);
        }
        return uint104(value);
    }

    /**
     * @dev Returns the downcasted uint96 from uint256, reverting on
     * overflow (when the input is greater than largest uint96).
     *
     * Counterpart to Solidity's `uint96` operator.
     *
     * Requirements:
     *
     * - input must fit into 96 bits
     */
    function toUint96(uint256 value) internal pure returns (uint96) {
        if (value > type(uint96).max) {
            revert SafeCastOverflowedUintDowncast(96, value);
        }
        return uint96(value);
    }

    /**
     * @dev Returns the downcasted uint88 from uint256, reverting on
     * overflow (when the input is greater than largest uint88).
     *
     * Counterpart to Solidity's `uint88` operator.
     *
     * Requirements:
     *
     * - input must fit into 88 bits
     */
    function toUint88(uint256 value) internal pure returns (uint88) {
        if (value > type(uint88).max) {
            revert SafeCastOverflowedUintDowncast(88, value);
        }
        return uint88(value);
    }

    /**
     * @dev Returns the downcasted uint80 from uint256, reverting on
     * overflow (when the input is greater than largest uint80).
     *
     * Counterpart to Solidity's `uint80` operator.
     *
     * Requirements:
     *
     * - input must fit into 80 bits
     */
    function toUint80(uint256 value) internal pure returns (uint80) {
        if (value > type(uint80).max) {
            revert SafeCastOverflowedUintDowncast(80, value);
        }
        return uint80(value);
    }

    /**
     * @dev Returns the downcasted uint72 from uint256, reverting on
     * overflow (when the input is greater than largest uint72).
     *
     * Counterpart to Solidity's `uint72` operator.
     *
     * Requirements:
     *
     * - input must fit into 72 bits
     */
    function toUint72(uint256 value) internal pure returns (uint72) {
        if (value > type(uint72).max) {
            revert SafeCastOverflowedUintDowncast(72, value);
        }
        return uint72(value);
    }

    /**
     * @dev Returns the downcasted uint64 from uint256, reverting on
     * overflow (when the input is greater than largest uint64).
     *
     * Counterpart to Solidity's `uint64` operator.
     *
     * Requirements:
     *
     * - input must fit into 64 bits
     */
    function toUint64(uint256 value) internal pure returns (uint64) {
        if (value > type(uint64).max) {
            revert SafeCastOverflowedUintDowncast(64, value);
        }
        return uint64(value);
    }

    /**
     * @dev Returns the downcasted uint56 from uint256, reverting on
     * overflow (when the input is greater than largest uint56).
     *
     * Counterpart to Solidity's `uint56` operator.
     *
     * Requirements:
     *
     * - input must fit into 56 bits
     */
    function toUint56(uint256 value) internal pure returns (uint56) {
        if (value > type(uint56).max) {
            revert SafeCastOverflowedUintDowncast(56, value);
        }
        return uint56(value);
    }

    /**
     * @dev Returns the downcasted uint48 from uint256, reverting on
     * overflow (when the input is greater than largest uint48).
     *
     * Counterpart to Solidity's `uint48` operator.
     *
     * Requirements:
     *
     * - input must fit into 48 bits
     */
    function toUint48(uint256 value) internal pure returns (uint48) {
        if (value > type(uint48).max) {
            revert SafeCastOverflowedUintDowncast(48, value);
        }
        return uint48(value);
    }

    /**
     * @dev Returns the downcasted uint40 from uint256, reverting on
     * overflow (when the input is greater than largest uint40).
     *
     * Counterpart to Solidity's `uint40` operator.
     *
     * Requirements:
     *
     * - input must fit into 40 bits
     */
    function toUint40(uint256 value) internal pure returns (uint40) {
        if (value > type(uint40).max) {
            revert SafeCastOverflowedUintDowncast(40, value);
        }
        return uint40(value);
    }

    /**
     * @dev Returns the downcasted uint32 from uint256, reverting on
     * overflow (when the input is greater than largest uint32).
     *
     * Counterpart to Solidity's `uint32` operator.
     *
     * Requirements:
     *
     * - input must fit into 32 bits
     */
    function toUint32(uint256 value) internal pure returns (uint32) {
        if (value > type(uint32).max) {
            revert SafeCastOverflowedUintDowncast(32, value);
        }
        return uint32(value);
    }

    /**
     * @dev Returns the downcasted uint24 from uint256, reverting on
     * overflow (when the input is greater than largest uint24).
     *
     * Counterpart to Solidity's `uint24` operator.
     *
     * Requirements:
     *
     * - input must fit into 24 bits
     */
    function toUint24(uint256 value) internal pure returns (uint24) {
        if (value > type(uint24).max) {
            revert SafeCastOverflowedUintDowncast(24, value);
        }
        return uint24(value);
    }

    /**
     * @dev Returns the downcasted uint16 from uint256, reverting on
     * overflow (when the input is greater than largest uint16).
     *
     * Counterpart to Solidity's `uint16` operator.
     *
     * Requirements:
     *
     * - input must fit into 16 bits
     */
    function toUint16(uint256 value) internal pure returns (uint16) {
        if (value > type(uint16).max) {
            revert SafeCastOverflowedUintDowncast(16, value);
        }
        return uint16(value);
    }

    /**
     * @dev Returns the downcasted uint8 from uint256, reverting on
     * overflow (when the input is greater than largest uint8).
     *
     * Counterpart to Solidity's `uint8` operator.
     *
     * Requirements:
     *
     * - input must fit into 8 bits
     */
    function toUint8(uint256 value) internal pure returns (uint8) {
        if (value > type(uint8).max) {
            revert SafeCastOverflowedUintDowncast(8, value);
        }
        return uint8(value);
    }

    /**
     * @dev Converts a signed int256 into an unsigned uint256.
     *
     * Requirements:
     *
     * - input must be greater than or equal to 0.
     */
    function toUint256(int256 value) internal pure returns (uint256) {
        if (value < 0) {
            revert SafeCastOverflowedIntToUint(value);
        }
        return uint256(value);
    }

    /**
     * @dev Returns the downcasted int248 from int256, reverting on
     * overflow (when the input is less than smallest int248 or
     * greater than largest int248).
     *
     * Counterpart to Solidity's `int248` operator.
     *
     * Requirements:
     *
     * - input must fit into 248 bits
     */
    function toInt248(int256 value) internal pure returns (int248 downcasted) {
        downcasted = int248(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(248, value);
        }
    }

    /**
     * @dev Returns the downcasted int240 from int256, reverting on
     * overflow (when the input is less than smallest int240 or
     * greater than largest int240).
     *
     * Counterpart to Solidity's `int240` operator.
     *
     * Requirements:
     *
     * - input must fit into 240 bits
     */
    function toInt240(int256 value) internal pure returns (int240 downcasted) {
        downcasted = int240(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(240, value);
        }
    }

    /**
     * @dev Returns the downcasted int232 from int256, reverting on
     * overflow (when the input is less than smallest int232 or
     * greater than largest int232).
     *
     * Counterpart to Solidity's `int232` operator.
     *
     * Requirements:
     *
     * - input must fit into 232 bits
     */
    function toInt232(int256 value) internal pure returns (int232 downcasted) {
        downcasted = int232(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(232, value);
        }
    }

    /**
     * @dev Returns the downcasted int224 from int256, reverting on
     * overflow (when the input is less than smallest int224 or
     * greater than largest int224).
     *
     * Counterpart to Solidity's `int224` operator.
     *
     * Requirements:
     *
     * - input must fit into 224 bits
     */
    function toInt224(int256 value) internal pure returns (int224 downcasted) {
        downcasted = int224(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(224, value);
        }
    }

    /**
     * @dev Returns the downcasted int216 from int256, reverting on
     * overflow (when the input is less than smallest int216 or
     * greater than largest int216).
     *
     * Counterpart to Solidity's `int216` operator.
     *
     * Requirements:
     *
     * - input must fit into 216 bits
     */
    function toInt216(int256 value) internal pure returns (int216 downcasted) {
        downcasted = int216(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(216, value);
        }
    }

    /**
     * @dev Returns the downcasted int208 from int256, reverting on
     * overflow (when the input is less than smallest int208 or
     * greater than largest int208).
     *
     * Counterpart to Solidity's `int208` operator.
     *
     * Requirements:
     *
     * - input must fit into 208 bits
     */
    function toInt208(int256 value) internal pure returns (int208 downcasted) {
        downcasted = int208(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(208, value);
        }
    }

    /**
     * @dev Returns the downcasted int200 from int256, reverting on
     * overflow (when the input is less than smallest int200 or
     * greater than largest int200).
     *
     * Counterpart to Solidity's `int200` operator.
     *
     * Requirements:
     *
     * - input must fit into 200 bits
     */
    function toInt200(int256 value) internal pure returns (int200 downcasted) {
        downcasted = int200(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(200, value);
        }
    }

    /**
     * @dev Returns the downcasted int192 from int256, reverting on
     * overflow (when the input is less than smallest int192 or
     * greater than largest int192).
     *
     * Counterpart to Solidity's `int192` operator.
     *
     * Requirements:
     *
     * - input must fit into 192 bits
     */
    function toInt192(int256 value) internal pure returns (int192 downcasted) {
        downcasted = int192(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(192, value);
        }
    }

    /**
     * @dev Returns the downcasted int184 from int256, reverting on
     * overflow (when the input is less than smallest int184 or
     * greater than largest int184).
     *
     * Counterpart to Solidity's `int184` operator.
     *
     * Requirements:
     *
     * - input must fit into 184 bits
     */
    function toInt184(int256 value) internal pure returns (int184 downcasted) {
        downcasted = int184(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(184, value);
        }
    }

    /**
     * @dev Returns the downcasted int176 from int256, reverting on
     * overflow (when the input is less than smallest int176 or
     * greater than largest int176).
     *
     * Counterpart to Solidity's `int176` operator.
     *
     * Requirements:
     *
     * - input must fit into 176 bits
     */
    function toInt176(int256 value) internal pure returns (int176 downcasted) {
        downcasted = int176(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(176, value);
        }
    }

    /**
     * @dev Returns the downcasted int168 from int256, reverting on
     * overflow (when the input is less than smallest int168 or
     * greater than largest int168).
     *
     * Counterpart to Solidity's `int168` operator.
     *
     * Requirements:
     *
     * - input must fit into 168 bits
     */
    function toInt168(int256 value) internal pure returns (int168 downcasted) {
        downcasted = int168(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(168, value);
        }
    }

    /**
     * @dev Returns the downcasted int160 from int256, reverting on
     * overflow (when the input is less than smallest int160 or
     * greater than largest int160).
     *
     * Counterpart to Solidity's `int160` operator.
     *
     * Requirements:
     *
     * - input must fit into 160 bits
     */
    function toInt160(int256 value) internal pure returns (int160 downcasted) {
        downcasted = int160(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(160, value);
        }
    }

    /**
     * @dev Returns the downcasted int152 from int256, reverting on
     * overflow (when the input is less than smallest int152 or
     * greater than largest int152).
     *
     * Counterpart to Solidity's `int152` operator.
     *
     * Requirements:
     *
     * - input must fit into 152 bits
     */
    function toInt152(int256 value) internal pure returns (int152 downcasted) {
        downcasted = int152(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(152, value);
        }
    }

    /**
     * @dev Returns the downcasted int144 from int256, reverting on
     * overflow (when the input is less than smallest int144 or
     * greater than largest int144).
     *
     * Counterpart to Solidity's `int144` operator.
     *
     * Requirements:
     *
     * - input must fit into 144 bits
     */
    function toInt144(int256 value) internal pure returns (int144 downcasted) {
        downcasted = int144(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(144, value);
        }
    }

    /**
     * @dev Returns the downcasted int136 from int256, reverting on
     * overflow (when the input is less than smallest int136 or
     * greater than largest int136).
     *
     * Counterpart to Solidity's `int136` operator.
     *
     * Requirements:
     *
     * - input must fit into 136 bits
     */
    function toInt136(int256 value) internal pure returns (int136 downcasted) {
        downcasted = int136(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(136, value);
        }
    }

    /**
     * @dev Returns the downcasted int128 from int256, reverting on
     * overflow (when the input is less than smallest int128 or
     * greater than largest int128).
     *
     * Counterpart to Solidity's `int128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     */
    function toInt128(int256 value) internal pure returns (int128 downcasted) {
        downcasted = int128(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(128, value);
        }
    }

    /**
     * @dev Returns the downcasted int120 from int256, reverting on
     * overflow (when the input is less than smallest int120 or
     * greater than largest int120).
     *
     * Counterpart to Solidity's `int120` operator.
     *
     * Requirements:
     *
     * - input must fit into 120 bits
     */
    function toInt120(int256 value) internal pure returns (int120 downcasted) {
        downcasted = int120(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(120, value);
        }
    }

    /**
     * @dev Returns the downcasted int112 from int256, reverting on
     * overflow (when the input is less than smallest int112 or
     * greater than largest int112).
     *
     * Counterpart to Solidity's `int112` operator.
     *
     * Requirements:
     *
     * - input must fit into 112 bits
     */
    function toInt112(int256 value) internal pure returns (int112 downcasted) {
        downcasted = int112(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(112, value);
        }
    }

    /**
     * @dev Returns the downcasted int104 from int256, reverting on
     * overflow (when the input is less than smallest int104 or
     * greater than largest int104).
     *
     * Counterpart to Solidity's `int104` operator.
     *
     * Requirements:
     *
     * - input must fit into 104 bits
     */
    function toInt104(int256 value) internal pure returns (int104 downcasted) {
        downcasted = int104(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(104, value);
        }
    }

    /**
     * @dev Returns the downcasted int96 from int256, reverting on
     * overflow (when the input is less than smallest int96 or
     * greater than largest int96).
     *
     * Counterpart to Solidity's `int96` operator.
     *
     * Requirements:
     *
     * - input must fit into 96 bits
     */
    function toInt96(int256 value) internal pure returns (int96 downcasted) {
        downcasted = int96(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(96, value);
        }
    }

    /**
     * @dev Returns the downcasted int88 from int256, reverting on
     * overflow (when the input is less than smallest int88 or
     * greater than largest int88).
     *
     * Counterpart to Solidity's `int88` operator.
     *
     * Requirements:
     *
     * - input must fit into 88 bits
     */
    function toInt88(int256 value) internal pure returns (int88 downcasted) {
        downcasted = int88(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(88, value);
        }
    }

    /**
     * @dev Returns the downcasted int80 from int256, reverting on
     * overflow (when the input is less than smallest int80 or
     * greater than largest int80).
     *
     * Counterpart to Solidity's `int80` operator.
     *
     * Requirements:
     *
     * - input must fit into 80 bits
     */
    function toInt80(int256 value) internal pure returns (int80 downcasted) {
        downcasted = int80(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(80, value);
        }
    }

    /**
     * @dev Returns the downcasted int72 from int256, reverting on
     * overflow (when the input is less than smallest int72 or
     * greater than largest int72).
     *
     * Counterpart to Solidity's `int72` operator.
     *
     * Requirements:
     *
     * - input must fit into 72 bits
     */
    function toInt72(int256 value) internal pure returns (int72 downcasted) {
        downcasted = int72(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(72, value);
        }
    }

    /**
     * @dev Returns the downcasted int64 from int256, reverting on
     * overflow (when the input is less than smallest int64 or
     * greater than largest int64).
     *
     * Counterpart to Solidity's `int64` operator.
     *
     * Requirements:
     *
     * - input must fit into 64 bits
     */
    function toInt64(int256 value) internal pure returns (int64 downcasted) {
        downcasted = int64(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(64, value);
        }
    }

    /**
     * @dev Returns the downcasted int56 from int256, reverting on
     * overflow (when the input is less than smallest int56 or
     * greater than largest int56).
     *
     * Counterpart to Solidity's `int56` operator.
     *
     * Requirements:
     *
     * - input must fit into 56 bits
     */
    function toInt56(int256 value) internal pure returns (int56 downcasted) {
        downcasted = int56(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(56, value);
        }
    }

    /**
     * @dev Returns the downcasted int48 from int256, reverting on
     * overflow (when the input is less than smallest int48 or
     * greater than largest int48).
     *
     * Counterpart to Solidity's `int48` operator.
     *
     * Requirements:
     *
     * - input must fit into 48 bits
     */
    function toInt48(int256 value) internal pure returns (int48 downcasted) {
        downcasted = int48(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(48, value);
        }
    }

    /**
     * @dev Returns the downcasted int40 from int256, reverting on
     * overflow (when the input is less than smallest int40 or
     * greater than largest int40).
     *
     * Counterpart to Solidity's `int40` operator.
     *
     * Requirements:
     *
     * - input must fit into 40 bits
     */
    function toInt40(int256 value) internal pure returns (int40 downcasted) {
        downcasted = int40(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(40, value);
        }
    }

    /**
     * @dev Returns the downcasted int32 from int256, reverting on
     * overflow (when the input is less than smallest int32 or
     * greater than largest int32).
     *
     * Counterpart to Solidity's `int32` operator.
     *
     * Requirements:
     *
     * - input must fit into 32 bits
     */
    function toInt32(int256 value) internal pure returns (int32 downcasted) {
        downcasted = int32(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(32, value);
        }
    }

    /**
     * @dev Returns the downcasted int24 from int256, reverting on
     * overflow (when the input is less than smallest int24 or
     * greater than largest int24).
     *
     * Counterpart to Solidity's `int24` operator.
     *
     * Requirements:
     *
     * - input must fit into 24 bits
     */
    function toInt24(int256 value) internal pure returns (int24 downcasted) {
        downcasted = int24(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(24, value);
        }
    }

    /**
     * @dev Returns the downcasted int16 from int256, reverting on
     * overflow (when the input is less than smallest int16 or
     * greater than largest int16).
     *
     * Counterpart to Solidity's `int16` operator.
     *
     * Requirements:
     *
     * - input must fit into 16 bits
     */
    function toInt16(int256 value) internal pure returns (int16 downcasted) {
        downcasted = int16(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(16, value);
        }
    }

    /**
     * @dev Returns the downcasted int8 from int256, reverting on
     * overflow (when the input is less than smallest int8 or
     * greater than largest int8).
     *
     * Counterpart to Solidity's `int8` operator.
     *
     * Requirements:
     *
     * - input must fit into 8 bits
     */
    function toInt8(int256 value) internal pure returns (int8 downcasted) {
        downcasted = int8(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(8, value);
        }
    }

    /**
     * @dev Converts an unsigned uint256 into a signed int256.
     *
     * Requirements:
     *
     * - input must be less than or equal to maxInt256.
     */
    function toInt256(uint256 value) internal pure returns (int256) {
        // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
        if (value > uint256(type(int256).max)) {
            revert SafeCastOverflowedUintToInt(value);
        }
        return int256(value);
    }
}

// SPDX-License-Identifier: Apache 2.0-or-later
pragma solidity >=0.8.0 <0.9.0;

import {SafeCast} from "@openzeppelin-legacy/contracts/utils/math/SafeCast.sol";
import {MixinStorage} from "../immutable/MixinStorage.sol";
import {IEOracle} from "../../extensions/adapters/interfaces/IEOracle.sol";
import {IERC20} from "../../interfaces/IERC20.sol";
import {IKyc} from "../../interfaces/IKyc.sol";
import {ISmartPoolActions} from "../../interfaces/v4/pool/ISmartPoolActions.sol";
import {AddressSet, EnumerableSet} from "../../libraries/EnumerableSet.sol";
import {NavImpactLib} from "../../libraries/NavImpactLib.sol";
import {ReentrancyGuardTransient} from "../../libraries/ReentrancyGuardTransient.sol";
import {SafeTransferLib} from "../../libraries/SafeTransferLib.sol";
import {VirtualStorageLib} from "../../libraries/VirtualStorageLib.sol";
import {NavComponents, NetAssetsValue} from "../../types/NavComponents.sol";

abstract contract MixinActions is MixinStorage, ReentrancyGuardTransient {
    using SafeTransferLib for address;
    using EnumerableSet for AddressSet;
    using SafeCast for uint256;

    error BaseTokenBalance();
    error PoolAmountSmallerThanMinimum(uint16 minimumOrderDivisor);
    error PoolBurnNotEnough();
    error PoolBurnNullAmount();
    error PoolBurnOutputAmount();
    error PoolCallerNotWhitelisted();
    error PoolMinimumPeriodNotEnough();
    error PoolMintAmountIn();
    error PoolMintInvalidRecipient();
    error PoolMintOutputAmount();
    error PoolTokenNotActive();
    error InvalidOperator();
    error PoolMintTokenNotActive();

    /*
     * EXTERNAL METHODS
     */
    /// @inheritdoc ISmartPoolActions
    function mint(
        address recipient,
        uint256 amountIn,
        uint256 amountOutMin
    ) external payable override nonReentrant returns (uint256 recipientAmount) {
        recipientAmount = _mint(recipient, amountIn, amountOutMin, _BASE_TOKEN_FLAG);
    }

    /// @inheritdoc ISmartPoolActions
    function mintWithToken(
        address recipient,
        uint256 amountIn,
        uint256 amountOutMin,
        address tokenIn
    ) external payable override nonReentrant returns (uint256 recipientAmount) {
        // Check owner has explicitly accepted this token for minting
        require(acceptedTokensSet().isActive(tokenIn), PoolMintTokenNotActive());
        // Token will be activated in _mint before the NAV snapshot.
        recipientAmount = _mint(recipient, amountIn, amountOutMin, tokenIn);
    }

    /// @inheritdoc ISmartPoolActions
    function burn(uint256 amountIn, uint256 amountOutMin) external override nonReentrant returns (uint256 netRevenue) {
        netRevenue = _burn(amountIn, amountOutMin, _BASE_TOKEN_FLAG);
    }

    /// @inheritdoc ISmartPoolActions
    function burnForToken(
        uint256 amountIn,
        uint256 amountOutMin,
        address tokenOut
    ) external override nonReentrant returns (uint256 netRevenue) {
        // early revert if token does not have price feed, REMOVED_ADDRESS_FLAG is sentinel for token not being active.
        require(activeTokensSet().isActive(tokenOut), PoolTokenNotActive());
        netRevenue = _burn(amountIn, amountOutMin, tokenOut);
    }

    /// @inheritdoc ISmartPoolActions
    /// @dev Reentrancy protection provided by calling functions (mint, burn, depositV3, donate)
    function updateUnitaryValue() external override returns (NetAssetsValue memory navParams) {
        NavComponents memory components = _updateNav();

        // Division by zero already handled in _updateNav() -> MixinPoolValue._updatePoolValue()
        // Zero supply returns stored NAV without update
        navParams = NetAssetsValue({
            unitaryValue: components.unitaryValue,
            netTotalValue: components.netTotalValue,
            netTotalLiabilities: components.netTotalLiabilities
        });
    }

    /// @inheritdoc ISmartPoolActions
    function setOperator(address operator, bool approved) external override returns (bool) {
        operators().isApproved[msg.sender][operator] = approved;

        emit OperatorSet(msg.sender, operator, approved);

        return true;
    }

    /*
     * PUBLIC METHODS
     */
    function decimals() public view virtual override returns (uint8);
    function isOperator(address holder, address operator) public view virtual returns (bool approved);

    /*
     * INTERNAL METHODS
     */
    function _updateNav() internal virtual returns (NavComponents memory);

    function _getFeeCollector() internal view virtual returns (address);

    function _getMinPeriod() internal view virtual returns (uint48);

    /// @dev Returns the spread, or _MAX_SPREAD if not set
    function _getSpread() internal view virtual returns (uint16);

    function _getTokenJar() internal view virtual returns (address);

    /*
     * PRIVATE METHODS
     */
    function _mint(
        address recipient,
        uint256 amountIn,
        uint256 amountOutMin,
        address tokenIn
    ) private returns (uint256) {
        require(recipient != _ZERO_ADDRESS, PoolMintInvalidRecipient());
        require(msg.sender == recipient || isOperator(recipient, msg.sender), InvalidOperator());

        // Activate tokenIn before the NAV snapshot to include any pre-existing untracked balance.
        if (tokenIn != _BASE_TOKEN_FLAG) {
            activeTokensSet().addUnique(IEOracle(address(this)), tokenIn, pool().baseToken);
        }

        NavComponents memory components = _updateNav();
        address kycProvider = poolParams().kycProvider;

        // require whitelisted user if kyc is enforced
        if (!kycProvider.isAddressZero()) {
            require(IKyc(kycProvider).isWhitelistedUser(recipient), PoolCallerNotWhitelisted());
        }

        _assertBiggerThanMinimum(amountIn);
        uint256 spread = (amountIn * _getSpread()) / _SPREAD_BASE;

        if (tokenIn == _BASE_TOKEN_FLAG) {
            tokenIn = components.baseToken;
        }

        if (tokenIn.isAddressZero()) {
            require(msg.value == amountIn, PoolMintAmountIn());
            _getTokenJar().safeTransferNative(spread);
        } else {
            tokenIn.safeTransferFrom(msg.sender, address(this), amountIn);
            tokenIn.safeTransfer(_getTokenJar(), spread);
        }

        amountIn -= spread;

        if (tokenIn != components.baseToken) {
            // convert the tokenIn amount into base token amount BEFORE calculating mintedAmount
            amountIn = uint256(
                IEOracle(address(this)).convertTokenAmount(tokenIn, amountIn.toInt256(), components.baseToken)
            );
        }

        uint256 mintedAmount = (amountIn * 10 ** components.decimals) / components.unitaryValue;
        poolTokens().totalSupply += mintedAmount;

        // allocate pool token transfers and log events.
        uint256 recipientAmount = _allocateMintTokens(recipient, mintedAmount);
        require(recipientAmount >= amountOutMin, PoolMintOutputAmount());
        return recipientAmount;
    }

    /// @notice Allocates tokens to recipient. Fee tokens are locked too.
    /// @dev Each new mint on same recipient sets new activation on all owned tokens.
    /// @param recipient Address of the recipient.
    /// @param mintedAmount Value of issued tokens.
    /// @return Amount of tokens minted to the recipient.
    function _allocateMintTokens(address recipient, uint256 mintedAmount) private returns (uint256) {
        uint48 activation;

        // it is safe to use unckecked as max min period is 30 days
        unchecked {
            activation = uint48(block.timestamp) + _getMinPeriod();
        }

        uint16 transactionFee = poolParams().transactionFee;

        if (transactionFee != 0) {
            address feeCollector = _getFeeCollector();

            if (feeCollector != recipient) {
                uint256 feePool = (mintedAmount * transactionFee) / _FEE_BASE;
                mintedAmount -= feePool;

                // fee tokens are locked as well
                accounts().userAccounts[feeCollector].userBalance += uint208(feePool);
                accounts().userAccounts[feeCollector].activation = activation;
                emit Transfer(_ZERO_ADDRESS, feeCollector, feePool);
            }
        }

        accounts().userAccounts[recipient].userBalance += uint208(mintedAmount);
        accounts().userAccounts[recipient].activation = activation;
        emit Transfer(_ZERO_ADDRESS, recipient, mintedAmount);
        return mintedAmount;
    }

    function _burn(uint256 amountIn, uint256 amountOutMin, address tokenOut) private returns (uint256 netRevenue) {
        require(amountIn > 0, PoolBurnNullAmount());
        UserAccount memory userAccount = accounts().userAccounts[msg.sender];
        require(userAccount.userBalance >= amountIn, PoolBurnNotEnough());
        require(block.timestamp >= userAccount.activation, PoolMinimumPeriodNotEnough());

        // update stored pool value
        NavComponents memory components = _updateNav();

        /// @notice allocate pool token transfers and log events.
        uint256 burntAmount = _allocateBurnTokens(amountIn, userAccount.userBalance);
        poolTokens().totalSupply -= burntAmount;

        // Post-burn safety check: ensure effective supply doesn't drop below minimum threshold
        // This prevents bypassing the EffectiveSupplyTooLow check via sequential burns
        int256 virtualSupply = VirtualStorageLib.getVirtualSupply();
        NavImpactLib.validateSupply(poolTokens().totalSupply, virtualSupply);

        // slither-disable-next-line divide-before-multiply
        netRevenue = (burntAmount * components.unitaryValue) / 10 ** decimals();

        address baseToken = pool().baseToken;

        if (tokenOut == _BASE_TOKEN_FLAG) {
            tokenOut = baseToken;
        } else if (tokenOut != baseToken) {
            // only allow arbitrary token redemption as a fallback in case the pool does not hold enough base currency
            uint256 baseTokenBalance = baseToken.isAddressZero()
                ? address(this).balance
                : IERC20(baseToken).balanceOf(address(this));
            require(netRevenue > baseTokenBalance, BaseTokenBalance());

            netRevenue = uint256(
                IEOracle(address(this)).convertTokenAmount(baseToken, netRevenue.toInt256(), tokenOut)
            );
        }

        uint256 spread = (netRevenue * _getSpread()) / _SPREAD_BASE;
        netRevenue -= spread;

        require(netRevenue >= amountOutMin, PoolBurnOutputAmount());

        if (tokenOut.isAddressZero()) {
            msg.sender.safeTransferNative(netRevenue);
            _getTokenJar().safeTransferNative(spread);
        } else {
            tokenOut.safeTransfer(msg.sender, netRevenue);
            tokenOut.safeTransfer(_getTokenJar(), spread);
        }
    }

    /// @notice Destroys tokens of holder.
    /// @dev Fee is paid in pool tokens, fee amount is not burnt.
    /// @param amountIn Value of tokens to be burnt.
    /// @param holderBalance The balance of the caller.
    /// @return Number of user burnt tokens.
    function _allocateBurnTokens(uint256 amountIn, uint256 holderBalance) private returns (uint256) {
        if (amountIn < holderBalance) {
            accounts().userAccounts[msg.sender].userBalance -= uint208(amountIn);
        } else {
            delete accounts().userAccounts[msg.sender];
        }

        if (poolParams().transactionFee != 0) {
            address feeCollector = _getFeeCollector();

            if (msg.sender != feeCollector) {
                uint256 feePool = (amountIn * poolParams().transactionFee) / _FEE_BASE;
                amountIn -= feePool;

                // allocate fee tokens to fee collector
                accounts().userAccounts[feeCollector].userBalance += uint208(feePool);
                accounts().userAccounts[feeCollector].activation = uint48(block.timestamp + 1);
                emit Transfer(msg.sender, feeCollector, feePool);
            }
        }

        emit Transfer(msg.sender, _ZERO_ADDRESS, amountIn);
        return amountIn;
    }

    function _assertBiggerThanMinimum(uint256 amount) private view {
        require(
            amount >= 10 ** decimals() / _MINIMUM_ORDER_DIVISOR,
            PoolAmountSmallerThanMinimum(_MINIMUM_ORDER_DIVISOR)
        );
    }
}

// SPDX-License-Identifier: Apache 2.0-or-later
pragma solidity >=0.8.0 <0.9.0;

import {MixinActions} from "./MixinActions.sol";
import {IEApps} from "../../extensions/adapters/interfaces/IEApps.sol";
import {IEOracle} from "../../extensions/adapters/interfaces/IEOracle.sol";
import {IERC20} from "../../interfaces/IERC20.sol";
import {ISmartPoolOwnerActions} from "../../interfaces/v4/pool/ISmartPoolOwnerActions.sol";
import {ApplicationsLib, ApplicationsSlot} from "../../libraries/ApplicationsLib.sol";
import {DelegationData, DelegationLib} from "../../libraries/DelegationLib.sol";
import {EnumerableSet, AddressSet} from "../../libraries/EnumerableSet.sol";
import {Delegation} from "../../types/Delegation.sol";
import {ExternalApp} from "../../types/ExternalApp.sol";

abstract contract MixinOwnerActions is MixinActions {
    using ApplicationsLib for ApplicationsSlot;
    using DelegationLib for DelegationData;
    using EnumerableSet for AddressSet;

    error PoolCallerIsNotOwner();
    error PoolFeeBiggerThanMax(uint16 maxFee);
    error PoolInputIsNotContract();
    error OwnerActionInputIsSameAsCurrent();
    error PoolLockupPeriodInvalid(uint48 minimum, uint48 maximum);
    error PoolNullOwnerInput();
    error PoolSpreadInvalid(uint16 maxSpread);

    modifier onlyOwner() {
        require(msg.sender == pool().owner, PoolCallerIsNotOwner());
        _;
    }

    /// @inheritdoc ISmartPoolOwnerActions
    function changeFeeCollector(address feeCollector) external override onlyOwner {
        // prevent the owner to set a fee collector without their prior consent
        require(msg.sender == feeCollector || isOperator(feeCollector, msg.sender), InvalidOperator());
        require(feeCollector != _getFeeCollector(), OwnerActionInputIsSameAsCurrent());
        poolParams().feeCollector = feeCollector;
        emit NewCollector(msg.sender, address(this), feeCollector);
    }

    /// @inheritdoc ISmartPoolOwnerActions
    /// @dev Minimum period is always at least 10 to prevent flash txs.
    function changeMinPeriod(uint48 minPeriod) external override onlyOwner {
        require(
            minPeriod >= _MIN_LOCKUP && minPeriod <= _MAX_LOCKUP,
            PoolLockupPeriodInvalid(_MIN_LOCKUP, _MAX_LOCKUP)
        );
        require(minPeriod != _getMinPeriod(), OwnerActionInputIsSameAsCurrent());
        poolParams().minPeriod = minPeriod;
        emit MinimumPeriodChanged(address(this), minPeriod);
    }

    /// @inheritdoc ISmartPoolOwnerActions
    function changeSpread(uint16 newSpread) external override onlyOwner {
        // 0 value is sentinel for uninitialized spread, returning _DEFAULT_SPREAD
        require(newSpread > 0 && newSpread <= _MAX_SPREAD, PoolSpreadInvalid(_MAX_SPREAD));
        require(newSpread != _getSpread(), OwnerActionInputIsSameAsCurrent());
        poolParams().spread = newSpread;
        emit SpreadChanged(address(this), newSpread);
    }

    function purgeInactiveTokensAndApps() external override onlyOwner {
        // retrieve the list and mapping of stored tokens
        AddressSet storage set = activeTokensSet();
        ApplicationsSlot storage appsBitmap = activeApplications();
        uint256 packedApps = appsBitmap.packedApplications;
        ExternalApp[] memory activeApps;

        try IEApps(address(this)).getAppTokenBalances(packedApps) returns (ExternalApp[] memory apps) {
            for (uint256 i = 0; i < apps.length; i++) {
                if (
                    apps[i].balances.length == 0 &&
                    ApplicationsLib.isActiveApplication(packedApps, uint256(apps[i].appType))
                ) {
                    appsBitmap.removeApplication(apps[i].appType);
                }
            }
            activeApps = apps;
        } catch Error(string memory reason) {
            // do not allow removing tokens if the apps do not return their tokens correctly
            revert(reason);
        }

        // base token is never pushed to active list for gas savings, we can safely remove any unactive token
        address[] memory activeTokens = set.addresses;

        // caching for gas savings
        uint256 activeTokensLength = activeTokens.length;
        uint256 activeTokenBalance;

        for (uint256 i = 0; i < activeTokensLength; i++) {
            bool inApp;

            // skip removal if a token is active in an application. Do not cache app length due to small number of supported applications.
            for (uint256 j = 0; j < activeApps.length; j++) {
                // caching for gas savings
                uint256 activeAppTokenBalancesLength = activeApps[j].balances.length;

                for (uint256 k = 0; k < activeAppTokenBalancesLength; k++) {
                    if (activeApps[j].balances[k].token == activeTokens[i]) {
                        inApp = true;
                        break; // Exit k loop
                    }
                }
                if (inApp) {
                    break; // Exit j loop if token found in any app
                }
            }

            if (!inApp) {
                if (activeTokens[i] == _ZERO_ADDRESS) {
                    activeTokenBalance = address(this).balance;
                } else {
                    activeTokenBalance = IERC20(activeTokens[i]).balanceOf(address(this));
                }

                if (activeTokenBalance <= 1) {
                    set.remove(activeTokens[i]);
                }
            }
        }
    }

    /// @inheritdoc ISmartPoolOwnerActions
    function revokeAllDelegations(address delegated) external override onlyOwner {
        DelegationData storage d = delegation();
        bytes4[] memory selectors = d.getSelectors(delegated);
        d.removeAllByAddress(delegated);
        uint256 len = selectors.length;
        for (uint256 i = 0; i < len; ++i) {
            emit DelegationUpdated(address(this), delegated, selectors[i], false);
        }
    }

    /// @inheritdoc ISmartPoolOwnerActions
    function revokeAllDelegationsForSelector(bytes4 selector) external override onlyOwner {
        DelegationData storage d = delegation();
        address[] memory addrs = d.getAddresses(selector);
        d.removeAllBySelector(selector);
        uint256 len = addrs.length;
        for (uint256 i = 0; i < len; ++i) {
            emit DelegationUpdated(address(this), addrs[i], selector, false);
        }
    }

    /// @inheritdoc ISmartPoolOwnerActions
    function setAcceptableMintToken(address token, bool isAccepted) external override onlyOwner {
        // acceptedTokensSet: controls mintWithToken permission (operator decision)
        // activeTokensSet: added when token is actually minted (see MixinActions._mint)
        AddressSet storage acceptedSet = acceptedTokensSet();

        if (isAccepted) {
            acceptedSet.addUnique(IEOracle(address(this)), token, pool().baseToken);
            // DO NOT add to activeTokensSet here - it will be added in _mint when tokens arrive
            // This prevents attack: accept token -> purge (removes from active) -> mint (NAV drops)
        } else {
            // Only remove from acceptedTokensSet, NOT from activeTokensSet
            // Removing from activeTokensSet would break NAV if pool owns the token
            acceptedSet.remove(token);
        }
    }

    /// @inheritdoc ISmartPoolOwnerActions
    function setKycProvider(address kycProvider) external override onlyOwner {
        // a pool can decide to remove the user whitelist requirement at any time
        if (kycProvider != address(0)) {
            require(_isContract(kycProvider), PoolInputIsNotContract());
        }
        require(kycProvider != poolParams().kycProvider, OwnerActionInputIsSameAsCurrent());
        poolParams().kycProvider = kycProvider;
        emit KycProviderSet(address(this), kycProvider);
    }

    /// @inheritdoc ISmartPoolOwnerActions
    function setOwner(address newOwner) external override onlyOwner {
        require(newOwner != _ZERO_ADDRESS, PoolNullOwnerInput());
        require(newOwner != pool().owner, OwnerActionInputIsSameAsCurrent());
        address oldOwner = pool().owner;
        pool().owner = newOwner;
        emit NewOwner(oldOwner, newOwner);
    }

    /// @inheritdoc ISmartPoolOwnerActions
    function setTransactionFee(uint16 transactionFee) external override onlyOwner {
        require(transactionFee <= _MAX_TRANSACTION_FEE, PoolFeeBiggerThanMax(_MAX_TRANSACTION_FEE)); //fee cannot be higher than 1%
        require(transactionFee != poolParams().transactionFee, OwnerActionInputIsSameAsCurrent());
        poolParams().transactionFee = transactionFee;
        emit NewFee(msg.sender, address(this), transactionFee);
    }

    /// @inheritdoc ISmartPoolOwnerActions
    function updateDelegation(Delegation[] calldata delegations) external override onlyOwner {
        DelegationData storage d = delegation();
        uint256 length = delegations.length;
        for (uint256 i = 0; i < length; ++i) {
            Delegation calldata entry = delegations[i];
            bool changed = entry.isDelegated
                ? d.add(entry.selector, entry.delegated)
                : d.remove(entry.selector, entry.delegated);
            if (changed) emit DelegationUpdated(address(this), entry.delegated, entry.selector, entry.isDelegated);
        }
    }

    function _isContract(address target) private view returns (bool) {
        return target.code.length > 0;
    }
}

File 5 of 48 : MixinConstants.sol
// SPDX-License-Identifier: Apache 2.0-or-later
pragma solidity >=0.8.0 <0.9.0;

import {ISmartPool} from "../../ISmartPool.sol";
import {ISmartPoolImmutable} from "../../interfaces/v4/pool/ISmartPoolImmutable.sol";
import {VirtualStorageLib} from "../../libraries/VirtualStorageLib.sol";

/// @notice Constants are copied in the bytecode and not assigned a storage slot, can safely be added to this contract.
/// @dev Inheriting from interface is required as we override public variables.
abstract contract MixinConstants is ISmartPool {
    /// @inheritdoc ISmartPoolImmutable
    string public constant override VERSION = "4.2.0";

    bytes32 internal constant _ACCEPTED_TOKENS_SLOT =
        0xa33198d1011bad6f8d9b4a537f82cf21cfac49b1430cf1a99c11aaf4d7325fc6;

    bytes32 internal constant _APPLICATIONS_SLOT = 0xdc487a67cca3fd0341a90d1b8834103014d2a61e6a212e57883f8680b8f9c831;

    bytes32 internal constant _OPERATOR_BOOLEAN_SLOT =
        0xac0ed3ab25c1c02fcfdfba47b1953f88a6f24e5a50f1076d09054047884e5350;

    bytes32 internal constant _POOL_ACCOUNTS_SLOT = 0xfd7547127f88410746fb7969b9adb4f9e9d8d2436aa2d2277b1103542deb7b8e;

    bytes32 internal constant _POOL_INIT_SLOT = 0xe48b9bb119adfc3bccddcc581484cc6725fe8d292ebfcec7d67b1f93138d8bd8;

    bytes32 internal constant _POOL_TOKENS_SLOT = 0xf46fb7ff9ff9a406787c810524417c818e45ab2f1997f38c2555c845d23bb9f6;

    bytes32 internal constant _POOL_VARIABLES_SLOT = 0xe3ed9e7d534645c345f2d15f0c405f8de0227b60eb37bbeb25b26db462415dec;

    bytes32 internal constant _TOKEN_REGISTRY_SLOT = 0x3dcde6752c7421366e48f002bbf8d6493462e0e43af349bebb99f0470a12300d;

    bytes32 internal constant _UNIV4_TOKEN_IDS_SLOT =
        0xd87266b00c1e82928c0b0200ad56e2ee648a35d4e9b273d2ac9533471e3b5d3c;

    bytes32 internal constant _VIRTUAL_SUPPLY_SLOT = VirtualStorageLib.VIRTUAL_SUPPLY_SLOT;

    bytes32 internal constant _DELEGATION_SLOT = 0x1de728329845ca9693f4e251833e4fd20a461e4f39179bee6e55171aedb6dc19;

    address internal constant _ZERO_ADDRESS = address(0);

    address internal constant _BASE_TOKEN_FLAG = address(1);

    uint16 internal constant _FEE_BASE = 10000;

    uint16 internal constant _MAX_SPREAD = 500; // +-5%, in basis points

    uint16 internal constant _DEFAULT_SPREAD = 10;

    uint16 internal constant _MAX_TRANSACTION_FEE = 100; // maximum 1%

    // minimum order size 1/1000th of base to avoid dust clogging things up
    uint16 internal constant _MINIMUM_ORDER_DIVISOR = 1e3;

    uint16 internal constant _SPREAD_BASE = 10000;

    uint48 internal constant _MAX_LOCKUP = 30 days;

    uint48 internal constant _MIN_LOCKUP = 1 days;
}

File 6 of 48 : MixinImmutables.sol
// SPDX-License-Identifier: Apache 2.0-or-later
pragma solidity >=0.8.0 <0.9.0;

import {MixinConstants} from "./MixinConstants.sol";
import {ISmartPoolImmutable} from "../../interfaces/v4/pool/ISmartPoolImmutable.sol";
import {IExtensionsMap} from "../../interfaces/IExtensionsMap.sol";

/// @notice Immutables are not assigned a storage slot, can be safely added to this contract.
abstract contract MixinImmutables is MixinConstants {
    error InvalidAuthorityInput();
    error InvalidExtensionsMapInput();

    /// @inheritdoc ISmartPoolImmutable
    address public immutable override authority;

    ///@inheritdoc ISmartPoolImmutable
    address public immutable override wrappedNative;

    ///@inheritdoc ISmartPoolImmutable
    address public immutable override tokenJar;

    // EIP1967 standard, must be immutable to be compile-time constant.
    address internal immutable _implementation;

    IExtensionsMap internal immutable _extensionsMap;

    /// @notice The ExtensionsMap interface is required to implement the expected methods as sanity check.
    constructor(address _authority, address extensionsMap, address _tokenJar) {
        require(_authority.code.length > 0, InvalidAuthorityInput());
        require(extensionsMap.code.length > 0, InvalidExtensionsMapInput());
        authority = _authority;

        _implementation = address(this);

        // initialize extensions mapping and assert it implements `getExtensionBySelector` method
        _extensionsMap = IExtensionsMap(extensionsMap);
        wrappedNative = _extensionsMap.wrappedNative();

        // the token jar input is expected to be correct at deployment, no sanity checks
        tokenJar = _tokenJar;

        // the following assertion will alway be true, as long as IExtensionsMap implements the expected methods.
        assert(
            IExtensionsMap.eApps.selector ^
                IExtensionsMap.eNavView.selector ^
                IExtensionsMap.eOracle.selector ^
                IExtensionsMap.eUpgrade.selector ^
                IExtensionsMap.eCrosschain.selector ^
                IExtensionsMap.wrappedNative.selector ^
                IExtensionsMap.getExtensionBySelector.selector ==
                type(IExtensionsMap).interfaceId
        );
    }
}

// SPDX-License-Identifier: Apache 2.0-or-later
pragma solidity >=0.8.0 <0.9.0;

import {MixinImmutables} from "./MixinImmutables.sol";
import {AddressSet, Pool} from "../../libraries/EnumerableSet.sol";
import {ApplicationsSlot} from "../../libraries/ApplicationsLib.sol";
import {DelegationData} from "../../libraries/DelegationLib.sol";

/// @notice Storage slots must be preserved to prevent storage clashing.
/// @dev Pool storage is not sequential: each variable is wrapped into a struct which is assigned a storage slot.
abstract contract MixinStorage is MixinImmutables {
    constructor() {
        // governance must always check that pool extensions are not using these storage slots (reserved for proxy storage)
        assert(_ACCEPTED_TOKENS_SLOT == bytes32(uint256(keccak256("pool.proxy.accepted.tokens")) - 1));
        assert(_APPLICATIONS_SLOT == bytes32(uint256(keccak256("pool.proxy.applications")) - 1));
        assert(_OPERATOR_BOOLEAN_SLOT == bytes32(uint256(keccak256("pool.proxy.operator.boolean")) - 1));
        assert(_POOL_ACCOUNTS_SLOT == bytes32(uint256(keccak256("pool.proxy.user.accounts")) - 1));
        assert(_POOL_INIT_SLOT == bytes32(uint256(keccak256("pool.proxy.initialization")) - 1));
        assert(_POOL_TOKENS_SLOT == bytes32(uint256(keccak256("pool.proxy.token")) - 1));
        assert(_POOL_VARIABLES_SLOT == bytes32(uint256(keccak256("pool.proxy.variables")) - 1));
        assert(_TOKEN_REGISTRY_SLOT == bytes32(uint256(keccak256("pool.proxy.token.registry")) - 1));
        assert(_UNIV4_TOKEN_IDS_SLOT == bytes32(uint256(keccak256("pool.proxy.uniV4.tokenIds")) - 1));
        assert(_VIRTUAL_SUPPLY_SLOT == bytes32(uint256(keccak256("pool.proxy.virtual.supply")) - 1));
        assert(_DELEGATION_SLOT == bytes32(uint256(keccak256("pool.proxy.delegation")) - 1));
    }

    struct Accounts {
        mapping(address owner => UserAccount) userAccounts;
    }

    function accounts() internal pure returns (Accounts storage s) {
        assembly {
            s.slot := _POOL_ACCOUNTS_SLOT
        }
    }

    function pool() internal pure returns (Pool storage s) {
        assembly {
            s.slot := _POOL_INIT_SLOT
        }
    }

    /// @notice Pool initialization struct wrapper.
    /// @dev Allows initializing pool as struct for better readability.
    /// @param pool The pool struct.
    struct PoolWrapper {
        Pool pool;
    }

    function poolWrapper() internal pure returns (PoolWrapper storage s) {
        assembly {
            s.slot := _POOL_INIT_SLOT
        }
    }

    function poolParams() internal pure returns (PoolParams storage s) {
        assembly {
            s.slot := _POOL_VARIABLES_SLOT
        }
    }

    function poolTokens() internal pure returns (PoolTokens storage s) {
        assembly {
            s.slot := _POOL_TOKENS_SLOT
        }
    }

    function activeTokensSet() internal pure returns (AddressSet storage s) {
        assembly {
            s.slot := _TOKEN_REGISTRY_SLOT
        }
    }

    function activeApplications() internal pure returns (ApplicationsSlot storage s) {
        assembly {
            s.slot := _APPLICATIONS_SLOT
        }
    }

    struct Operator {
        mapping(address holder => mapping(address operator => bool isApproved)) isApproved;
    }

    function operators() internal pure returns (Operator storage s) {
        assembly {
            s.slot := _OPERATOR_BOOLEAN_SLOT
        }
    }

    function delegation() internal pure returns (DelegationData storage s) {
        assembly {
            s.slot := _DELEGATION_SLOT
        }
    }

    function acceptedTokensSet() internal pure returns (AddressSet storage s) {
        assembly {
            s.slot := _ACCEPTED_TOKENS_SLOT
        }
    }
}

// SPDX-License-Identifier: Apache 2.0-or-later
pragma solidity >=0.8.0 <0.9.0;

import {MixinPoolValue} from "../state/MixinPoolValue.sol";
import {IERC20} from "../../interfaces/IERC20.sol";
import {ISmartPoolState} from "../../interfaces/v4/pool/ISmartPoolState.sol";
import {DelegationData, DelegationLib} from "../../libraries/DelegationLib.sol";
import {Pool} from "../../libraries/EnumerableSet.sol";

abstract contract MixinPoolState is MixinPoolValue {
    using DelegationLib for DelegationData;
    /*
     * EXTERNAL VIEW METHODS
     */
    /// @inheritdoc IERC20
    function balanceOf(address who) external view override returns (uint256) {
        return accounts().userAccounts[who].userBalance;
    }

    /// @inheritdoc ISmartPoolState
    function getAcceptedMintTokens() external view override returns (address[] memory tokens) {
        return _getAcceptedMintTokens();
    }

    /// @inheritdoc ISmartPoolState
    /// @dev Grg staking and UniV3 positions will not be returned by default.
    function getActiveApplications() external view override returns (uint256 packedApplications) {
        return _getActiveApplications();
    }

    /// @inheritdoc ISmartPoolState
    function getActiveTokens() external view override returns (ActiveTokens memory tokens) {
        return _getActiveTokens();
    }

    /// @inheritdoc ISmartPoolState
    function getPoolStorage()
        external
        view
        override
        returns (ReturnedPool memory poolInitParams, PoolParams memory poolVariables, PoolTokens memory poolTokensInfo)
    {
        return (getPool(), getPoolParams(), getPoolTokens());
    }

    /// @inheritdoc ISmartPoolState
    function getUserAccount(address who) external view override returns (UserAccount memory) {
        return accounts().userAccounts[who];
    }

    /// @inheritdoc ISmartPoolState
    function name() external view override returns (string memory) {
        return pool().name;
    }

    /// @inheritdoc ISmartPoolState
    function owner() external view override returns (address) {
        return pool().owner;
    }

    /// @inheritdoc ISmartPoolState
    function totalSupply() external view override returns (uint256) {
        return poolTokens().totalSupply;
    }

    /*
     * PUBLIC VIEW METHODS
     */
    /// @inheritdoc IERC20
    function decimals() public view override returns (uint8) {
        return pool().decimals;
    }

    /// @inheritdoc ISmartPoolState
    function getPool() public view override returns (ReturnedPool memory) {
        Pool memory pool = pool();
        // we return symbol as string, omit unlocked as always true
        return
            ReturnedPool({
                name: pool.name,
                symbol: symbol(),
                decimals: pool.decimals,
                owner: pool.owner,
                baseToken: pool.baseToken
            });
    }

    /// @inheritdoc ISmartPoolState
    function getPoolParams() public view override returns (PoolParams memory) {
        return
            PoolParams({
                minPeriod: _getMinPeriod(),
                spread: _getSpread(),
                transactionFee: poolParams().transactionFee,
                feeCollector: _getFeeCollector(),
                kycProvider: poolParams().kycProvider
            });
    }

    /// @inheritdoc ISmartPoolState
    function getPoolTokens() public view override returns (PoolTokens memory) {
        uint256 unitaryValue = poolTokens().unitaryValue;
        return
            PoolTokens({
                unitaryValue: unitaryValue != 0 ? unitaryValue : 10 ** pool().decimals,
                totalSupply: poolTokens().totalSupply
            });
    }

    /// @inheritdoc ISmartPoolState
    function symbol() public view override returns (string memory) {
        bytes8 _symbol = pool().symbol;
        uint8 i = 0;
        while (i < 8 && _symbol[i] != 0) {
            i++;
        }
        bytes memory bytesArray = new bytes(i);
        for (i = 0; i < 8 && _symbol[i] != 0; i++) {
            bytesArray[i] = _symbol[i];
        }
        return string(bytesArray);
    }

    /// @inheritdoc ISmartPoolState
    function isOperator(address holder, address operator) public view override returns (bool) {
        return operators().isApproved[holder][operator];
    }

    /// @inheritdoc ISmartPoolState
    function getDelegatedAddresses(bytes4 selector) external view override returns (address[] memory) {
        return delegation().getAddresses(selector);
    }

    /// @inheritdoc ISmartPoolState
    function getDelegatedSelectors(address delegated) external view override returns (bytes4[] memory) {
        return delegation().getSelectors(delegated);
    }

    /*
     * INTERNAL VIEW METHODS
     */
    function _getActiveApplications() internal view override returns (uint256) {
        return activeApplications().packedApplications;
    }

    function _getFeeCollector() internal view override returns (address) {
        address feeCollector = poolParams().feeCollector;
        return feeCollector != _ZERO_ADDRESS ? feeCollector : pool().owner;
    }

    function _getMinPeriod() internal view override returns (uint48) {
        uint48 minPeriod = poolParams().minPeriod;
        return minPeriod != 0 ? minPeriod : _MAX_LOCKUP;
    }

    function _getSpread() internal view override returns (uint16) {
        uint16 spread = poolParams().spread;
        return spread != 0 ? spread : _DEFAULT_SPREAD;
    }

    function _getTokenJar() internal view override returns (address) {
        return tokenJar;
    }

    function _getAcceptedMintTokens() private view returns (address[] memory) {
        return acceptedTokensSet().addresses;
    }

    function _getActiveTokens() private view returns (ActiveTokens memory tokens) {
        tokens.activeTokens = activeTokensSet().addresses;
        tokens.baseToken = pool().baseToken;
    }
}

// SPDX-License-Identifier: Apache-2.0-or-later
pragma solidity >=0.8.0 <0.9.0;

import {SafeCast} from "@openzeppelin-legacy/contracts/utils/math/SafeCast.sol";
import {MixinOwnerActions} from "../actions/MixinOwnerActions.sol";
import {IEApps} from "../../extensions/adapters/interfaces/IEApps.sol";
import {IEOracle} from "../../extensions/adapters/interfaces/IEOracle.sol";
import {IERC20} from "../../interfaces/IERC20.sol";
import {AddressSet, EnumerableSet} from "../../libraries/EnumerableSet.sol";
import {ApplicationsLib, ApplicationsSlot} from "../../libraries/ApplicationsLib.sol";
import {NavImpactLib} from "../../libraries/NavImpactLib.sol";
import {SlotDerivation} from "../../libraries/SlotDerivation.sol";
import {TransientStorage} from "../../libraries/TransientStorage.sol";
import {VirtualStorageLib} from "../../libraries/VirtualStorageLib.sol";
import {ExternalApp} from "../../types/ExternalApp.sol";
import {NavComponents} from "../../types/NavComponents.sol";

/// @title MixinPoolValue
/// @notice A contract that retrieves smart pool token balances and computes their base token value.
abstract contract MixinPoolValue is MixinOwnerActions {
    using ApplicationsLib for ApplicationsSlot;
    using EnumerableSet for AddressSet;
    using SlotDerivation for bytes32;
    using TransientStorage for address;
    using SafeCast for uint256;
    using SafeCast for int256;

    error BaseTokenPriceFeedError();

    /// @notice Uses transient storage to keep track of unique token balances.
    /// @dev With null total supply a pool will return the last stored value.
    function _updateNav() internal override returns (NavComponents memory components) {
        components.unitaryValue = poolTokens().unitaryValue;
        components.totalSupply = poolTokens().totalSupply;
        components.baseToken = pool().baseToken;
        components.decimals = pool().decimals;

        // make sure we can later convert token values in base token. Asserted before anything else to prevent potential holder burn failure.
        // Notice: the following check adds a little gas overhead, but is necessary to guarantee backwards compatibility with v3. Because all existing
        // v3 vaults have a price feed, we could move the following assertion to the following block, i.e. executing it only on the first mint.
        require(IEOracle(address(this)).hasPriceFeed(components.baseToken), BaseTokenPriceFeedError());

        // Always compute net total assets (used for cross-chain donation validation)
        int256 netValue = _computeTotalPoolValue(components.baseToken);

        if (netValue >= 0) {
            components.netTotalValue = uint256(netValue);
        } else {
            components.netTotalLiabilities = uint256(-netValue);
        }

        // first mint skips nav calculation
        // slither-disable-next-line incorrect-equality
        if (components.unitaryValue == 0) {
            components.unitaryValue = 10 ** components.decimals;
        } else {
            int256 virtualSupply = VirtualStorageLib.getVirtualSupply();

            // revert if abs virtual supply below a minimum threshold of total supply. Also means effective supply must be non-negative.
            NavImpactLib.validateSupply(components.totalSupply, virtualSupply);
            int256 effectiveSupply = int256(components.totalSupply) + virtualSupply;

            // effective supply cannot be negative from previous assertion. Also cannot burn internally more than has been minted.
            // slither-disable-next-line incorrect-equality
            if (effectiveSupply == 0) {
                return components;
            }

            components.totalSupply = uint256(effectiveSupply);

            if (components.netTotalValue > 0) {
                components.unitaryValue =
                    (components.netTotalValue * 10 ** components.decimals) /
                    components.totalSupply;
            } else {
                return components;
            }
        }

        // never allow storing 0 - flag for default unitary price of uninitialized pool
        // slither-disable-next-line incorrect-equality
        if (components.unitaryValue == 0) {
            components.unitaryValue = 1;
        }

        // update storage only if different
        if (components.unitaryValue != poolTokens().unitaryValue) {
            poolTokens().unitaryValue = components.unitaryValue;
            emit NewNav(msg.sender, address(this), components.unitaryValue);
        }
    }

    /// @notice Updates the stored value with an updated one.
    /// @param baseToken The address of the base token.
    /// @return poolValue The total value of the pool in base token units.
    /// @dev Assumes the stored list contain unique elements.
    /// @dev A write method to be used in mint and burn operations.
    /// @dev Uses transient storage to keep track of unique token balances.
    function _computeTotalPoolValue(address baseToken) private returns (int256 poolValue) {
        uint256 packedApps = activeApplications().packedApplications;

        // Declare reusable variables outside loops to reduce stack depth
        address token;
        int256 amount;

        // try and get positions balances. Will revert if not successul and prevent incorrect nav calculation.
        try IEApps(address(this)).getAppTokenBalances(_getActiveApplications()) returns (ExternalApp[] memory apps) {
            // position balances can be negative, positive, or null (handled explicitly later)
            for (uint256 i = 0; i < apps.length; i++) {
                // caching for gas savings
                uint256 appTokenBalancesLength = apps[i].balances.length;

                // active positions tokens are a subset of active tokens
                for (uint256 j = 0; j < appTokenBalancesLength; j++) {
                    // push application if not active but tokens are returned from it (as with GRG staking and univ3 liquidity)
                    if (!ApplicationsLib.isActiveApplication(packedApps, uint256(apps[i].appType))) {
                        activeApplications().storeApplication(apps[i].appType);
                    }

                    // Reuse variables to minimize stack depth
                    amount = apps[i].balances[j].amount;

                    // Always add or update the balance from positions
                    if (amount != 0) {
                        token = apps[i].balances[j].token;

                        // cache balances in temporary storage
                        int256 storedBalance = token.getBalance();

                        // verify token in active tokens set, add it otherwise (relevant for pool deployed before v4)
                        if (storedBalance == 0) {
                            // will add to set only if not already stored
                            activeTokensSet().addUnique(IEOracle(address(this)), token, baseToken);
                        }

                        storedBalance += amount;
                        // store balance and make sure slot is not cleared to prevent trying to add token again
                        token.storeBalance(storedBalance != 0 ? storedBalance : int256(1));
                    }
                }
            }
        } catch Error(string memory reason) {
            // we prevent returning pool value when any of the tracked applications fails, as they are not expected to
            revert(reason);
        }

        // initialize pool value as base token balances (wallet balance plus apps balances)
        uint256 nativeAmount = msg.value;
        poolValue = _getAndClearBalance(baseToken, nativeAmount);

        // active tokens include any potentially not stored app token, like when a pool upgrades from v3 to v4
        address[] memory activeTokens = activeTokensSet().addresses;

        // caching for gas savings
        uint256 activeTokensLength = activeTokens.length;
        int256[] memory tokenAmounts = new int256[](activeTokensLength);

        // base token is not stored in activeTokens array
        for (uint256 i = 0; i < activeTokensLength; i++) {
            tokenAmounts[i] = _getAndClearBalance(activeTokens[i], nativeAmount);
        }

        if (activeTokensLength > 0) {
            poolValue += IEOracle(address(this)).convertBatchTokenAmounts(activeTokens, tokenAmounts, baseToken);
        }
    }

    /// @dev Returns 0 balance if ERC20 call fails.
    /// @param token The token address to get balance for
    /// @param nativeAmount The msg.value to subtract from ETH balance (passed to avoid multiple msg.value reads)
    function _getAndClearBalance(address token, uint256 nativeAmount) private returns (int256 value) {
        value = token.getBalance();

        // clear temporary storage if used
        if (value != 0) {
            token.storeBalance(0);
        }

        // the active tokens list contains unique addresses
        if (token == _ZERO_ADDRESS) {
            value += (address(this).balance - nativeAmount).toInt256();
        } else {
            try IERC20(token).balanceOf(address(this)) returns (uint256 _balance) {
                value += _balance.toInt256();
            } catch {
                // returns 0 balance if the ERC20 balance cannot be found
                return 0;
            }
        }
    }

    /// virtual methods
    function _getActiveApplications() internal view virtual returns (uint256);
}

// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.8.0 <0.9.0;

import {IStorageAccessible} from "../../interfaces/v4/pool/IStorageAccessible.sol";

/// @title StorageAccessible - generic base contract that allows callers to access all internal storage.
/// @notice See https://github.com/gnosis/util-contracts/blob/bb5fe5fb5df6d8400998094fb1b32a178a47c3a1/contracts/StorageAccessible.sol
abstract contract MixinStorageAccessible is IStorageAccessible {
    /// @inheritdoc IStorageAccessible
    function getStorageAt(uint256 offset, uint256 length) public view override returns (bytes memory) {
        bytes memory result = new bytes(length * 32);
        for (uint256 index = 0; index < length; index++) {
            // solhint-disable-next-line no-inline-assembly
            assembly {
                let word := sload(add(offset, index))
                mstore(add(add(result, 0x20), mul(index, 0x20)), word)
            }
        }
        return result;
    }

    /// @inheritdoc IStorageAccessible
    function getStorageSlotsAt(uint256[] memory slots) public view override returns (bytes memory) {
        // caching for gas savings
        uint256 slotsLength = slots.length;

        bytes memory result = new bytes(slotsLength * 32);
        for (uint256 index = 0; index < slotsLength; index++) {
            uint256 slot = slots[index];
            // solhint-disable-next-line no-inline-assembly
            assembly {
                let word := sload(slot)
                mstore(add(add(result, 0x20), mul(index, 0x20)), word)
            }
        }
        return result;
    }
}

// SPDX-License-Identifier: Apache 2.0
pragma solidity >=0.8.0 <0.9.0;

import {IERC20} from "../../interfaces/IERC20.sol";

/// @notice This contract makes it easy for clients to track ERC20.
abstract contract MixinAbstract is IERC20 {
    /// @dev Non-implemented ERC20 method.
    function transfer(address to, uint256 value) external override returns (bool success) {}

    /// @dev Non-implemented ERC20 method.
    function transferFrom(address from, address to, uint256 value) external override returns (bool success) {}

    /// @dev Non-implemented ERC20 method.
    function approve(address spender, uint256 value) external override returns (bool success) {}

    /// @dev Non-implemented ERC20 method.
    function allowance(address owner, address spender) external view override returns (uint256) {}
}

File 12 of 48 : MixinFallback.sol
// SPDX-License-Identifier: Apache 2.0
pragma solidity >=0.8.0 <0.9.0;

import {MixinImmutables} from "../immutable/MixinImmutables.sol";
import {MixinStorage} from "../immutable/MixinStorage.sol";
import {IMinimumVersion} from "../../extensions/adapters/interfaces/IMinimumVersion.sol";
import {IAuthority} from "../../interfaces/IAuthority.sol";
import {ISmartPoolFallback} from "../../interfaces/v4/pool/ISmartPoolFallback.sol";
import {VersionLib} from "../../libraries/VersionLib.sol";

abstract contract MixinFallback is MixinImmutables, MixinStorage {
    using VersionLib for string;

    error PoolImplementationDirectCallNotAllowed();
    error PoolMethodNotAllowed();
    error PoolVersionNotSupported();

    // reading immutable through internal method more gas efficient
    modifier onlyDelegateCall() {
        _checkDelegateCall();
        _;
    }

    /* solhint-disable no-complex-fallback */
    /// @inheritdoc ISmartPoolFallback
    /// @dev Extensions are persistent, while adapters are upgradable by the governance.
    /// @dev uses shouldDelegatecall to flag selectors that should prompt a delegatecall.
    fallback() external onlyDelegateCall {
        // returns nil target if selector not mapped. Uses delegatecall to preserve context of msg.sender for shouldDelegatecall flag
        (, bytes memory returnData) = address(_extensionsMap).delegatecall(
            abi.encodeCall(_extensionsMap.getExtensionBySelector, (msg.sig))
        );
        (address target, bool shouldDelegatecall) = abi.decode(returnData, (address, bool));

        if (target == _ZERO_ADDRESS) {
            target = IAuthority(authority).getApplicationAdapter(msg.sig);

            // we check that the method is approved by governance
            require(target != _ZERO_ADDRESS, PoolMethodNotAllowed());

            // use try statement, as previously deployed adapters do not implement the method and are supported
            try IMinimumVersion(target).requiredVersion() returns (string memory required) {
                require(VERSION.isVersionHigherOrEqual(required), PoolVersionNotSupported());
            } catch {}

            // adapter calls: approved delegates for their specific selectors in write mode,
            // owner in write mode, read mode for everyone else (including this contract).
            shouldDelegatecall =
                delegation().selectorToAddressPosition[msg.sig][msg.sender] != 0 ||
                msg.sender == pool().owner;
        }

        assembly {
            calldatacopy(0, 0, calldatasize())
            let success
            if eq(shouldDelegatecall, 1) {
                success := delegatecall(gas(), target, 0, calldatasize(), 0, 0)
                returndatacopy(0, 0, returndatasize())
                if eq(success, 0) {
                    revert(0, returndatasize())
                }
                return(0, returndatasize())
            }
            success := staticcall(gas(), target, 0, calldatasize(), 0, 0)
            returndatacopy(0, 0, returndatasize())
            // we allow the staticcall to revert with rich error, should we want to add errors to extensions view methods
            if eq(success, 0) {
                revert(0, returndatasize())
            }
            return(0, returndatasize())
        }
    }

    /* solhint-enable no-complex-fallback */

    /// @inheritdoc ISmartPoolFallback
    receive() external payable onlyDelegateCall {}

    function _checkDelegateCall() private view {
        require(address(this) != _implementation, PoolImplementationDirectCallNotAllowed());
    }
}

File 13 of 48 : MixinInitializer.sol
// SPDX-License-Identifier: Apache 2.0
pragma solidity >=0.8.0 <0.9.0;

import {MixinImmutables} from "../immutable/MixinImmutables.sol";
import {MixinStorage} from "../immutable/MixinStorage.sol";
import {IERC20} from "../../interfaces/IERC20.sol";
import {IRigoblockPoolProxyFactory} from "../../interfaces/IRigoblockPoolProxyFactory.sol";
import {ISmartPoolInitializer} from "../../interfaces/v4/pool/ISmartPoolInitializer.sol";
import {Pool} from "../../libraries/EnumerableSet.sol";

abstract contract MixinInitializer is MixinImmutables, MixinStorage {
    error BaseTokenDecimals();
    error PoolAlreadyInitialized();

    modifier onlyUninitialized() {
        // pool proxy is always initialized in the constructor, therefore
        // empty code means the pool has not been initialized
        require(address(this).code.length == 0, PoolAlreadyInitialized());
        _;
    }

    /// @inheritdoc ISmartPoolInitializer
    /// @dev Cannot be reentered as no non-view call is performed to external contracts. Unlocked is kept for backwards compatibility.
    function initializePool() external override onlyUninitialized {
        IRigoblockPoolProxyFactory.Parameters memory initParams = IRigoblockPoolProxyFactory(msg.sender).parameters();

        Pool memory pool = Pool({
            name: initParams.name,
            symbol: initParams.symbol,
            decimals: 18,
            owner: initParams.owner,
            unlocked: true,
            baseToken: initParams.baseToken
        });

        // overwrite token decimals
        if (initParams.baseToken != _ZERO_ADDRESS) {
            assert(initParams.baseToken.code.length > 0);
            try IERC20(initParams.baseToken).decimals() returns (uint8 decimals) {
                // a pool with small decimals could easily underflow.
                assert(decimals >= 6);

                // update with the base token's decimals
                pool.decimals = decimals;
            } catch {
                revert BaseTokenDecimals();
            }
        }

        // initialize storage
        poolWrapper().pool = pool;
        emit PoolInitialized(msg.sender, initParams.owner, initParams.baseToken, initParams.name, initParams.symbol);
    }
}

// SPDX-License-Identifier: Apache 2.0
pragma solidity >=0.8.0 <0.9.0;

import {ExternalApp} from "../../../types/ExternalApp.sol";

interface IEApps {
    /// @notice Returns token balances owned in a set of external contracts.
    /// @param packedApplications The uint encoded bitmap flags of the active applications.
    /// @return appBalances The arrays of lists of token balances grouped by application type.
    function getAppTokenBalances(uint256 packedApplications) external returns (ExternalApp[] memory appBalances);

    /// @notice Returns the pool's Uniswap V4 active liquidity positions.
    /// @return tokenIds Array of liquidity position token ids.
    function getUniV4TokenIds() external view returns (uint256[] memory tokenIds);
}

// SPDX-License-Identifier: Apache-2.0-or-later
pragma solidity >=0.8.0 <0.9.0;

import {DestinationMessageParams} from "../../../types/Crosschain.sol";

/// @title ECrosschain Interface - Handles incoming cross-chain transfers and refunds from expired deposits.
/// @author Gabriele Rigo - <[email protected]>
interface IECrosschain {
    /// @notice Emitted when cross-chain tokens are received
    /// @param from Address that initiated the cross-chain transfer (SpokePool)
    /// @param token Token received
    /// @param amount Amount received (actual balance delta)
    /// @param opType Operation type (0=Transfer, 1=Sync)
    event TokensReceived(address indexed from, address indexed token, uint256 amount, uint8 indexed opType);

    /// @notice Emitted when virtual supply is modified
    /// @param adjustment Signed adjustment (+/-)
    /// @param newSupply New virtual supply after adjustment
    event VirtualSupplyUpdated(int256 adjustment, int256 newSupply);

    error InvalidOpType();
    error DonationLock(bool locked);
    error BalanceUnderflow();
    error NavManipulationDetected(uint256 expectedNav, uint256 actualNav);
    error TokenNotInitialized();

    /// @notice Handles receiving tokens from a cross-chain message or an escrow refund.
    /// @dev Called via delegatecall from pool. Callable by anyone.
    /// @param token The token received on this chain.
    /// @param amount The amount received.
    /// @param params The message params from the source calls sent to the across multicall handler.
    function donate(address token, uint256 amount, DestinationMessageParams calldata params) external;
}

// SPDX-License-Identifier: Apache-2.0-or-later
pragma solidity >=0.8.0 <0.9.0;

interface IEOracle {
    /// @notice Returns the sum of the token amounts converted to a target token.
    /// @dev Will first try to convert via cross with chain currency, fallback to direct cross if not available.
    /// @param tokens The array of token addresses to be converted.
    /// @param amounts The array of amounts to be converted.
    /// @param targetToken The address of the target token.
    /// @return totalConvertedAmount The total value of converted amount in target token amount.
    function convertBatchTokenAmounts(
        address[] calldata tokens,
        int256[] calldata amounts,
        address targetToken
    ) external view returns (int256 totalConvertedAmount);

    /// @notice Returns a token amount converted to a target token.
    /// @dev Will first try to convert via cross with chain currency, fallback to direct cross if not available.
    /// @param token The address of the token to be converted.
    /// @param amount The amount to be converted.
    /// @param targetToken The address of the target token.
    /// @return convertedAmount The value of converted amount in target token amount.
    function convertTokenAmount(
        address token,
        int256 amount,
        address targetToken
    ) external view returns (int256 convertedAmount);

    /// @notice Returns whether a token has a price feed.
    /// @param token The address of the token.
    /// @return Boolean the price feed exists.
    function hasPriceFeed(address token) external view returns (bool);

    /// @notice Returns token price aginst native currency.
    /// @param token The address of the token.
    /// @return twap The time weighted average price.
    function getTwap(address token) external view returns (int24 twap);
}

File 17 of 48 : IMinimumVersion.sol
// SPDX-License-Identifier: Apache 2.0
pragma solidity >=0.8.0 <0.9.0;

interface IMinimumVersion {
    /// @notice Returns the minimum implementation version to use an external application.
    /// @dev Adapters must implement it when modifying proxy state or storage.
    /// @return String of the minimum supported version.
    function requiredVersion() external view returns (string memory);
}

// SPDX-License-Identifier: Apache 2.0
/*

 Copyright 2017-2018 RigoBlock, Rigo Investment Sagl.

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.

*/

pragma solidity >=0.7.0 <0.9.0;

/// @title Authority Interface - Allows interaction with the Authority contract.
/// @author Gabriele Rigo - <[email protected]>
// solhint-disable-next-line
interface IAuthority {
    /// @notice Adds a permission for a role.
    /// @dev Possible roles are Role.ADAPTER, Role.FACTORY, Role.WHITELISTER
    /// @param from Address of the method caller.
    /// @param target Address of the approved wallet.
    /// @param permissionType Enum type of permission.
    event PermissionAdded(address indexed from, address indexed target, uint8 indexed permissionType);

    /// @notice Removes a permission for a role.
    /// @dev Possible roles are Role.ADAPTER, Role.FACTORY, Role.WHITELISTER
    /// @param from Address of the  method caller.
    /// @param target Address of the approved wallet.
    /// @param permissionType Enum type of permission.
    event PermissionRemoved(address indexed from, address indexed target, uint8 indexed permissionType);

    /// @notice Removes an approved method.
    /// @dev Removes a mapping of method selector to adapter according to eip1967.
    /// @param from Address of the  method caller.
    /// @param adapter Address of the adapter.
    /// @param selector Bytes4 of the method signature.
    event RemovedMethod(address indexed from, address indexed adapter, bytes4 indexed selector);

    /// @notice Approves a new method.
    /// @dev Adds a mapping of method selector to adapter according to eip1967.
    /// @param from Address of the  method caller.
    /// @param adapter  Address of the adapter.
    /// @param selector Bytes4 of the method signature.
    event WhitelistedMethod(address indexed from, address indexed adapter, bytes4 indexed selector);

    enum Role {
        ADAPTER,
        FACTORY,
        WHITELISTER
    }

    /// @notice Mapping of permission type to bool.
    /// @param Mapping of type of permission to bool is authorized.
    struct Permission {
        mapping(Role => bool) authorized;
    }

    /// @notice Allows a whitelister to whitelist a method.
    /// @param selector Bytes4 hex of the method selector.
    /// @param adapter Address of the adapter implementing the method.
    /// @notice We do not save list of approved as better queried by events.
    function addMethod(bytes4 selector, address adapter) external;

    /// @notice Allows a whitelister to remove a method.
    /// @param selector Bytes4 hex of the method selector.
    /// @param adapter Address of the adapter implementing the method.
    function removeMethod(bytes4 selector, address adapter) external;

    /// @notice Allows owner to set extension adapter address.
    /// @param adapter Address of the target adapter.
    /// @param isWhitelisted Bool whitelisted.
    function setAdapter(address adapter, bool isWhitelisted) external;

    /// @notice Allows an admin to set factory permission.
    /// @param factory Address of the target factory.
    /// @param isWhitelisted Bool whitelisted.
    function setFactory(address factory, bool isWhitelisted) external;

    /// @notice Allows the owner to set whitelister permission.
    /// @param whitelister Address of the whitelister.
    /// @param isWhitelisted Bool whitelisted.
    /// @notice Whitelister permission is required to approve methods in extensions adapter.
    function setWhitelister(address whitelister, bool isWhitelisted) external;

    /// @notice Returns the address of the adapter associated to the signature.
    /// @param selector Hex of the method signature.
    /// @return Address of the adapter.
    function getApplicationAdapter(bytes4 selector) external view returns (address);

    /// @notice Provides whether a factory is whitelisted.
    /// @param target Address of the target factory.
    /// @return Bool is whitelisted.
    function isWhitelistedFactory(address target) external view returns (bool);

    /// @notice Provides whether an address is whitelister.
    /// @param target Address of the target whitelister.
    /// @return Bool is whitelisted.
    function isWhitelister(address target) external view returns (bool);
}

// SPDX-License-Identifier: Apache 2.0
/*

 Copyright 2018 RigoBlock, Rigo Investment Sagl.

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.

*/

pragma solidity >=0.8.0 <0.9.0;

interface IERC20 {
    /// @notice Emitted when a token is transferred.
    /// @param from Address transferring the tokens.
    /// @param to Address receiving the tokens.
    /// @param value Number of token units.
    event Transfer(address indexed from, address indexed to, uint256 value);

    /// @notice Emitted when a token holder sets and approval.
    /// @param owner Address of the account setting the approval.
    /// @param spender Address of the allowed account.
    /// @param value Number of approved units.
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /// @notice Transfers token from holder to another address.
    /// @param to Address to send tokens to.
    /// @param value Number of token units to send.
    /// @return success Bool the transaction was successful.
    function transfer(address to, uint256 value) external returns (bool success);

    /// @notice Allows spender to transfer tokens from the holder.
    /// @param from Address of the token holder.
    /// @param to Address to send tokens to.
    /// @param value Number of units to transfer.
    /// @return success Bool the transaction was successful.
    function transferFrom(
        address from,
        address to,
        uint256 value
    ) external returns (bool success);

    /// @notice Allows a holder to approve a spender.
    /// @param spender Address of the token spender.
    /// @param value Number of units to be approved.
    /// @return success Bool the transaction was successful.
    function approve(address spender, uint256 value) external returns (bool success);

    /// @notice Returns token balance for an address.
    /// @param who Address to query balance for.
    /// @return Number of units held.
    function balanceOf(address who) external view returns (uint256);

    /// @notice Returns token allowance of an address to another address.
    /// @param owner Address of token hodler.
    /// @param spender Address of the token spender.
    /// @return Number of allowed units.
    function allowance(address owner, address spender) external view returns (uint256);

    /// @notice Returns token decimals.
    /// @return Uint8 number of decimals.
    function decimals() external view returns (uint8);
}

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

/// @title IExtensionsMap - Wraps extensions selectors to addresses.
/// @author Gabriele Rigo - <[email protected]>
interface IExtensionsMap {
    /// @notice Returns the address of the applications extension contract.
    function eApps() external view returns (address);

    /// @notice Returns the address of the navigation view extension contract.
    function eNavView() external view returns (address);

    /// @notice Returns the address of the oracle extension contract
    function eOracle() external view returns (address);

    /// @notice Returns the address of the upgrade extension contract.
    function eUpgrade() external view returns (address);

    /// @notice Returns the address of the cross-chain handler extension contract.
    function eCrosschain() external view returns (address);

    /// @notice Returns the address of the wrapped native token.
    /// @dev It is used for initializing it in the pool implementation immutable storage without passing it in the constructor.
    function wrappedNative() external view returns (address);

    /// @notice Returns the map of an extension's selector.
    /// @dev Stores all extensions selectors and addresses in its bytecode for gas efficiency.
    /// @param selector Selector of the function signature.
    /// @return extension Address of the target extensions.
    /// @return shouldDelegatecall Boolean if should maintain context of call or not.
    function getExtensionBySelector(bytes4 selector) external view returns (address extension, bool shouldDelegatecall);
}

// SPDX-License-Identifier: Apache 2.0
/*

 Copyright 2017-2018 RigoBlock, Rigo Investment Sagl.

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.

*/

pragma solidity >=0.8.0 <0.9.0;

/// @title KycFace - allows interaction with a Kyc provider.
/// @author Gabriele Rigo - <[email protected]>
// solhint-disable-next-line
interface IKyc {
    /// @notice Returns whether an address has been whitelisted.
    /// @param user The address to verify.
    /// @return Bool the user is whitelisted.
    function isWhitelistedUser(address user) external view returns (bool);
}

// SPDX-License-Identifier: Apache-2.0-or-later
/*

 Copyright 2017-2022 RigoBlock, Rigo Investment Sagl, Rigo Intl.

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.

*/

pragma solidity >=0.8.0 <0.9.0;

/// @title Pool Proxy Factory Interface - Allows external interaction with Pool Proxy Factory.
/// @author Gabriele Rigo - <[email protected]>
// solhint-disable-next-line
interface IRigoblockPoolProxyFactory {
    /// @notice Emitted when a new pool is created.
    /// @param poolAddress Address of the new pool.
    event PoolCreated(address poolAddress);

    /// @notice Emitted when a new implementation is set by the Rigoblock Dao.
    /// @param implementation Address of the new implementation.
    event Upgraded(address indexed implementation);

    /// @notice Emitted when registry address is upgraded by the Rigoblock Dao.
    /// @param registry Address of the new registry.
    event RegistryUpgraded(address indexed registry);

    /// @notice Returns the implementation address for the pool proxies.
    /// @return Address of the implementation.
    function implementation() external view returns (address);

    /// @notice Creates a new Rigoblock pool.
    /// @param name String of the name.
    /// @param symbol String of the symbol.
    /// @param baseToken Address of the base token.
    /// @return newPoolAddress Address of the new pool.
    /// @return poolId Id of the new pool.
    function createPool(
        string calldata name,
        string calldata symbol,
        address baseToken
    ) external returns (address newPoolAddress, bytes32 poolId);

    /// @notice Allows Rigoblock Dao to update factory pool implementation.
    /// @param newImplementation Address of the new implementation contract.
    function setImplementation(address newImplementation) external;

    /// @notice Allows owner to update the registry.
    /// @param newRegistry Address of the new registry.
    function setRegistry(address newRegistry) external;

    /// @notice Returns the address of the pool registry.
    /// @return Address of the registry.
    function getRegistry() external view returns (address);

    /// @notice Pool initialization parameters.
    /// @params name String of the name (max 31 characters).
    /// @params symbol bytes8 symbol.
    /// @params owner Address of the owner.
    /// @params baseToken Address of the base token.
    struct Parameters {
        string name;
        bytes8 symbol;
        address owner;
        address baseToken;
    }

    /// @notice Returns the pool initialization parameters at proxy deploy.
    /// @return Tuple of the pool parameters.
    function parameters() external view returns (Parameters memory);
}

// SPDX-License-Identifier: Apache 2.0-or-later
pragma solidity >=0.8.0 <0.9.0;

import {NetAssetsValue} from "../../../types/NavComponents.sol";

/// @title Rigoblock V3 Pool Actions Interface - Allows interaction with the pool contract.
/// @author Gabriele Rigo - <[email protected]>
// solhint-disable-next-line
interface ISmartPoolActions {
    /// @notice Allows a user to mint pool tokens on behalf of an address.
    /// @param recipient Address receiving the tokens.
    /// @param amountIn Amount of base tokens.
    /// @param amountOutMin Minimum amount to be received, prevents pool operator frontrunning.
    /// @return recipientAmount Number of tokens minted to recipient.
    function mint(
        address recipient,
        uint256 amountIn,
        uint256 amountOutMin
    ) external payable returns (uint256 recipientAmount);

    /// @notice Allows a user to mint pool tokens on behalf of an address using a desired token.
    /// @dev The token must be vault-owned, i.e. in the active token list, after operator action.
    /// @param recipient Address receiving the tokens.
    /// @param amountIn Amount of base tokens.
    /// @param amountOutMin Minimum amount to be received, prevents pool operator frontrunning.
    /// @return recipientAmount Number of tokens minted to recipient.
    function mintWithToken(
        address recipient,
        uint256 amountIn,
        uint256 amountOutMin,
        address tokenIn
    ) external payable returns (uint256 recipientAmount);

    /// @notice Allows a pool holder to burn pool tokens.
    /// @param amountIn Number of tokens to burn.
    /// @param amountOutMin Minimum amount to be received, prevents pool operator frontrunning.
    /// @return netRevenue Net amount of burnt pool tokens.
    function burn(uint256 amountIn, uint256 amountOutMin) external returns (uint256 netRevenue);

    /// @notice Allows a pool holder to burn pool tokens and receive a token other than base token.
    /// @dev The method is a fallback for when the vault does not hold enough base token, reverts otherwise.
    /// @param amountIn Number of tokens to burn.
    /// @param amountOutMin Minimum amount to be received, prevents pool operator frontrunning.
    /// @param tokenOut The token to be received in exchange for pool tokens.
    /// @return netRevenue Net amount of burnt pool tokens.
    function burnForToken(
        uint256 amountIn,
        uint256 amountOutMin,
        address tokenOut
    ) external returns (uint256 netRevenue);

    /// @notice Allows anyone to store an up-to-date pool price.
    /// @return navParams Tuple of unitary value, net total value, net total liabilities.
    function updateUnitaryValue() external returns (NetAssetsValue memory navParams);

    /// @notice Sets or removes an operator for the caller.
    /// @param operator The address of the operator.
    /// @param approved The approval status.
    /// @return bool True, always.
    function setOperator(address operator, bool approved) external returns (bool);
}

File 24 of 48 : ISmartPoolEvents.sol
// SPDX-License-Identifier: Apache 2.0-or-later
pragma solidity >=0.8.0 <0.9.0;

/// @title Rigoblock V3 Pool Events - Declares events of the pool contract.
/// @author Gabriele Rigo - <[email protected]>
interface ISmartPoolEvents {
    /// @notice Emitted when a new pool is initialized.
    /// @dev Pool is initialized at new pool creation.
    /// @param group Address of the factory.
    /// @param owner Address of the owner.
    /// @param baseToken Address of the base token.
    /// @param name String name of the pool.
    /// @param symbol String symbol of the pool.
    event PoolInitialized(
        address indexed group,
        address indexed owner,
        address indexed baseToken,
        string name,
        bytes8 symbol
    );

    /// @notice Emitted when new owner is set.
    /// @param old Address of the previous owner.
    /// @param current Address of the new owner.
    event NewOwner(address indexed old, address indexed current);

    /// @notice Emitted when NAV storage is updated.
    /// @param sender Address of the wallet prompting an update.
    /// @param pool Address of the pool.
    /// @param unitaryValue Value of 1 token in wei units.
    event NewNav(address indexed sender, address indexed pool, uint256 unitaryValue);

    /// @notice Emitted when pool operator sets new mint fee.
    /// @param pool Address of the pool.
    /// @param who Address that is sending the transaction.
    /// @param transactionFee Number of the new fee in wei.
    event NewFee(address indexed pool, address indexed who, uint16 transactionFee);

    /// @notice Emitted when pool operator updates fee collector address.
    /// @param pool Address of the pool.
    /// @param who Address that is sending the transaction.
    /// @param feeCollector Address of the new fee collector.
    event NewCollector(address indexed pool, address indexed who, address feeCollector);

    /// @notice Emitted when pool operator updates minimum holding period.
    /// @param pool Address of the pool.
    /// @param minimumPeriod Number of seconds.
    event MinimumPeriodChanged(address indexed pool, uint48 minimumPeriod);

    /// @notice Emitted when pool operator updates the mint/burn spread.
    /// @param pool Address of the pool.
    /// @param spread Number of the spread in basis points.
    event SpreadChanged(address indexed pool, uint16 spread);

    /// @notice Emitted when pool operator sets a kyc provider.
    /// @param pool Address of the pool.
    /// @param kycProvider Address of the kyc provider.
    event KycProviderSet(address indexed pool, address indexed kycProvider);

    /// @notice Emitted when a user sets an operator.
    /// @param holder Address of the user.
    /// @param operator Address of the operator.
    /// @param approved Boolean the operator is approved by the user.
    event OperatorSet(address indexed holder, address indexed operator, bool approved);

    /// @notice Emitted when a selector delegation is added or removed by the pool owner.
    /// @param pool Address of the pool.
    /// @param delegated Address whose delegation status changed.
    /// @param selector Function selector whose access was updated.
    /// @param isDelegated True if access was granted, false if revoked.
    event DelegationUpdated(address indexed pool, address indexed delegated, bytes4 indexed selector, bool isDelegated);

    /// @notice Emitted when a token's active status changes in the pool.
    /// @param token Address of the token whose status changed.
    /// @param isActive True if token was activated, false if deactivated.
    event TokenStatusChanged(address indexed token, bool isActive);
}

File 25 of 48 : ISmartPoolFallback.sol
// SPDX-License-Identifier: Apache 2.0-or-later
pragma solidity >=0.8.0 <0.9.0;

/// @title Rigoblock V3 Pool Fallback Interface - Interface of the fallback method.
/// @author Gabriele Rigo - <[email protected]>
interface ISmartPoolFallback {
    /// @notice Delegate calls to pool extension.
    /// @dev Delegatecall restricted to owner, staticcall accessible by everyone.
    /// @dev Restricting delegatecall to owner effectively locks direct calls.
    fallback() external;

    /// @notice Allows transfers to pool.
    /// @dev Prevents accidental transfer to implementation contract.
    receive() external payable;
}

// SPDX-License-Identifier: Apache 2.0-or-later
pragma solidity >=0.8.0 <0.9.0;

/// @title Rigoblock V3 Pool Immutable - Interface of the pool storage.
/// @author Gabriele Rigo - <[email protected]>
interface ISmartPoolImmutable {
    /// @notice Returns a string of the pool version.
    /// @return String of the pool implementation version.
    function VERSION() external view returns (string memory);

    /// @notice Returns the address of the authority contract.
    /// @return Address of the authority contract.
    function authority() external view returns (address);

    /// @notice Returns the address of the WETH9 contract.
    /// @dev Used to convert WETH balances to ETH without executing an oracle call.
    /// @return Address of the WETH9 contract.
    function wrappedNative() external view returns (address);

    /// @notice Returns the address of the Rigoblock token jar contract.
    /// @dev Used to transfer protocol fees to the buy-back-and-burn contract.
    /// @return Address of the token jar contract.
    function tokenJar() external view returns (address);
}

File 27 of 48 : ISmartPoolInitializer.sol
// SPDX-License-Identifier: Apache 2.0-or-later
pragma solidity >=0.8.0 <0.9.0;

/// @title Rigoblock V3 Pool Initializer Interface - Allows initializing a pool contract.
/// @author Gabriele Rigo - <[email protected]>
// solhint-disable-next-line
interface ISmartPoolInitializer {
    /// @notice Initializes to pool storage.
    /// @dev Pool can only be initialized at creation, meaning this method cannot be called directly to implementation.
    function initializePool() external;
}

// SPDX-License-Identifier: Apache 2.0-or-later
pragma solidity >=0.8.0 <0.9.0;

import {Delegation} from "../../../types/Delegation.sol";

/// @title Rigoblock V3 Pool Owner Actions Interface - Interface of the owner methods.
/// @author Gabriele Rigo - <[email protected]>
interface ISmartPoolOwnerActions {
    /// @notice Allows owner to decide where to receive the fee.
    /// @param feeCollector Address of the fee receiver.
    function changeFeeCollector(address feeCollector) external;

    /// @notice Allows pool owner to change the minimum holding period.
    /// @param minPeriod Time in seconds.
    function changeMinPeriod(uint48 minPeriod) external;

    /// @notice Allows pool owner to change the mint/burn spread.
    /// @param newSpread Number between 0 and 1000, in basis points.
    function changeSpread(uint16 newSpread) external;

    /// @notice Allows the owner to remove all inactive token and applications.
    /// @dev This is the only endpoint that has access to removing a token from the active tokens tuple.
    /// @dev Used to reduce cost of mint/burn as more tokens are traded, and allow lower gas for hft.
    function purgeInactiveTokensAndApps() external;

    /// @notice Allows the owner to set acceptable mint tokens other than the base token.
    /// @param token Address of the target token.
    /// @param isAccepted Boolean to indicate whether the token is to be added or removed from storage.
    function setAcceptableMintToken(address token, bool isAccepted) external;

    /// @notice Allows pool owner to set/update the user whitelist contract.
    /// @dev Kyc provider can be set to null, removing user whitelist requirement.
    /// @param kycProvider Address if the kyc provider.
    function setKycProvider(address kycProvider) external;

    /// @notice Allows pool owner to set a new owner address.
    /// @dev Method restricted to owner.
    /// @param newOwner Address of the new owner.
    function setOwner(address newOwner) external;

    /// @notice Allows pool owner to set the transaction fee.
    /// @param transactionFee Value of the transaction fee in basis points.
    function setTransactionFee(uint16 transactionFee) external;

    /// @notice Allows pool owner to batch grant or revoke delegated adapter write access.
    /// @dev Each entry independently adds or removes one (selector, address) pair.
    ///      Emits DelegationUpdated only for entries that change storage (idempotent operations emit no event).
    /// @param delegations Array of delegation operations to apply.
    function updateDelegation(Delegation[] calldata delegations) external;


    /// @notice Revokes all selector delegations for a given address in a single call.
    /// @dev Useful when a delegated wallet is compromised.
    /// @param delegated Address whose full delegation is to be revoked.
    function revokeAllDelegations(address delegated) external;

    /// @notice Revokes all address delegations for a given selector in a single call.
    /// @dev Useful when an adapter is being replaced by governance and stale delegates should be cleaned.
    /// @param selector Selector whose full delegation list is to be cleared.
    function revokeAllDelegationsForSelector(bytes4 selector) external;
}

// SPDX-License-Identifier: Apache 2.0-or-later
pragma solidity >=0.8.0 <0.9.0;

/// @title Rigoblock V3 Pool State - Returns the pool view methods.
/// @author Gabriele Rigo - <[email protected]>
interface ISmartPoolState {
    /// @notice Returns the list of accepted mint tokens.
    /// @return tokens Array of token addresses.
    function getAcceptedMintTokens() external view returns (address[] memory tokens);

    /// @notice Returns the active application flags.
    /// @return packedApplications Packed value of bitmap encoded active flags.
    function getActiveApplications() external view returns (uint256 packedApplications);

    struct ActiveTokens {
        address[] activeTokens;
        address baseToken;
    }

    /// @notice Returns the list of active tokens and the base token.
    /// @return tokens Tuple of active tokens list and base token.
    /// @dev Base token is always active.
    function getActiveTokens() external view returns (ActiveTokens memory tokens);

    /// @notice Returned pool initialization parameters.
    /// @dev Symbol is stored as bytes8 but returned as string to facilitating client view.
    /// @param name String of the pool name (max 32 characters).
    /// @param symbol String of the pool symbol (from 3 to 5 characters).
    /// @param decimals Uint8 decimals.
    /// @param owner Address of the pool operator.
    /// @param baseToken Address of the base token of the pool (0 for base currency).
    struct ReturnedPool {
        string name;
        string symbol;
        uint8 decimals;
        address owner;
        address baseToken;
    }

    /// @notice Returns the struct containing pool initialization parameters.
    /// @dev Symbol is stored as bytes8 but returned as string in the returned struct, unlocked is omitted as alwasy true.
    /// @return ReturnedPool struct.
    function getPool() external view returns (ReturnedPool memory);

    /// @notice Pool variables.
    /// @param minPeriod Minimum holding period in seconds.
    /// @param spread Value of spread in basis points (from 0 to +-10%).
    /// @param transactionFee Value of transaction fee in basis points (from 0 to 1%).
    /// @param feeCollector Address of the fee receiver.
    /// @param kycProvider Address of the kyc provider.
    struct PoolParams {
        uint48 minPeriod;
        uint16 spread;
        uint16 transactionFee;
        address feeCollector;
        address kycProvider;
    }

    /// @notice Returns the struct compaining pool parameters.
    /// @return PoolParams struct.
    function getPoolParams() external view returns (PoolParams memory);

    /// @notice Pool tokens.
    /// @param unitaryValue A token's unitary value in base token.
    /// @param totalSupply Number of total issued pool tokens.
    struct PoolTokens {
        uint256 unitaryValue;
        uint256 totalSupply;
    }

    /// @notice Returns the struct containing pool tokens info.
    /// @return PoolTokens struct.
    /// @notice Unitary value is the last stored unitary value.
    function getPoolTokens() external view returns (PoolTokens memory);

    /// @notice Returns the aggregate pool generic storage.
    /// @return poolInitParams The pool's initialization parameters.
    /// @return poolVariables The pool's variables.
    /// @return poolTokensInfo The pool's tokens info.
    function getPoolStorage()
        external
        view
        returns (ReturnedPool memory poolInitParams, PoolParams memory poolVariables, PoolTokens memory poolTokensInfo);

    /// @notice Pool holder account.
    /// @param userBalance Number of tokens held by user.
    /// @param activation Time when tokens become active.
    struct UserAccount {
        uint208 userBalance;
        uint48 activation;
    }

    /// @notice Returns a pool holder's account struct.
    /// @return UserAccount struct.
    function getUserAccount(address _who) external view returns (UserAccount memory);

    /// @notice Returns a string of the pool name.
    /// @dev Name maximum length 31 bytes.
    /// @return String of the name.
    function name() external view returns (string memory);

    /// @notice Returns the address of the owner.
    /// @return Address of the owner.
    function owner() external view returns (address);

    /// @notice Returns a string of the pool symbol.
    /// @return String of the symbol.
    function symbol() external view returns (string memory);

    /// @notice Returns the total amount of issued tokens for this pool.
    /// @return Number of total issued tokens.
    function totalSupply() external view returns (uint256);

    /// @param holder The address of the holder.
    /// @param operator The address of the operator.
    /// @return approved The approval status.
    function isOperator(address holder, address operator) external view returns (bool approved);

    /// @notice Returns all addresses currently granted delegated write access to a selector.
    /// @param selector The adapter function selector to query.
    /// @return addresses Array of addresses with delegated access for that selector.
    function getDelegatedAddresses(bytes4 selector) external view returns (address[] memory addresses);

    /// @notice Returns all selectors currently delegated to an address.
    /// @param delegated The address to query.
    /// @return selectors Array of selectors the address has been granted access to.
    function getDelegatedSelectors(address delegated) external view returns (bytes4[] memory selectors);
}

// SPDX-License-Identifier: Apache 2.0-or-later
pragma solidity >=0.8.0 <0.9.0;

/// @title IStorageAccessible - generic base interface that allows callers to access all internal storage.
/// @notice See https://github.com/gnosis/util-contracts/blob/bb5fe5fb5df6d8400998094fb1b32a178a47c3a1/contracts/StorageAccessible.sol
interface IStorageAccessible {
    /// @notice Reads `length` bytes of storage in the currents contract.
    /// @param offset - the offset in the current contract's storage in words to start reading from.
    /// @param length - the number of words (32 bytes) of data to read.
    /// @return Bytes string of the bytes that were read.
    function getStorageAt(uint256 offset, uint256 length) external view returns (bytes memory);

    /// @notice Reads bytes of storage at different storage locations.
    /// @dev Returns a string with values regarless of where they are stored, i.e. variable, mapping or struct.
    /// @param slots The array of storage slots to query into.
    /// @return Bytes string composite of different storage locations' value.
    function getStorageSlotsAt(uint256[] memory slots) external view returns (bytes memory);
}

File 31 of 48 : ISmartPool.sol
// SPDX-License-Identifier: Apache-2.0-or-later
pragma solidity >=0.8.0 <0.9.0;

import {IERC20} from "./interfaces/IERC20.sol";
import {ISmartPoolActions} from "./interfaces/v4/pool/ISmartPoolActions.sol";
import {ISmartPoolEvents} from "./interfaces/v4/pool/ISmartPoolEvents.sol";
import {ISmartPoolFallback} from "./interfaces/v4/pool/ISmartPoolFallback.sol";
import {ISmartPoolImmutable} from "./interfaces/v4/pool/ISmartPoolImmutable.sol";
import {ISmartPoolInitializer} from "./interfaces/v4/pool/ISmartPoolInitializer.sol";
import {ISmartPoolOwnerActions} from "./interfaces/v4/pool/ISmartPoolOwnerActions.sol";
import {ISmartPoolState} from "./interfaces/v4/pool/ISmartPoolState.sol";
import {IStorageAccessible} from "./interfaces/v4/pool/IStorageAccessible.sol";

/// @title Rigoblock V3 Pool Interface - Allows interaction with the pool contract.
/// @author Gabriele Rigo - <[email protected]>
// solhint-disable-next-line
interface ISmartPool is
    IERC20,
    ISmartPoolImmutable,
    ISmartPoolEvents,
    ISmartPoolFallback,
    ISmartPoolInitializer,
    ISmartPoolActions,
    ISmartPoolOwnerActions,
    ISmartPoolState,
    IStorageAccessible
{}

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

import {Applications} from "../types/Applications.sol";

struct ApplicationsSlot {
    uint256 packedApplications;
}

library ApplicationsLib {
    error ApplicationIndexBitmaskRange();

    uint256 private constant MAX_ALLOWED_APPLICATIONS = 255;

    /// @notice Sets an application as active in the bitmask.
    /// @param self The storage slot where the packed applications are stored.
    /// @param appIndex The application to set as active.
    function storeApplication(ApplicationsSlot storage self, uint256 appIndex) internal {
        require(appIndex < MAX_ALLOWED_APPLICATIONS, ApplicationIndexBitmaskRange());
        uint256 flag = 1 << appIndex;
        self.packedApplications |= flag;
    }

    /// @notice Removes an application from being active in the bitmask.
    /// @param self The storage slot where the packed applications are stored.
    /// @param appIndex The application to remove.
    function removeApplication(ApplicationsSlot storage self, uint256 appIndex) internal {
        require(appIndex < MAX_ALLOWED_APPLICATIONS, ApplicationIndexBitmaskRange());
        uint256 flag = ~(1 << appIndex);
        self.packedApplications &= flag;
    }

    /// @notice Checks if an application is active in the bitmask.
    /// @param packedApplications The bitmap packed active applications flags.
    /// @param appIndex The application to check.
    /// @return bool Whether the application is active.
    function isActiveApplication(uint256 packedApplications, uint256 appIndex) internal pure returns (bool) {
        require(appIndex < MAX_ALLOWED_APPLICATIONS, ApplicationIndexBitmaskRange());
        uint256 flag = 1 << appIndex;
        return (packedApplications & flag) != 0;
    }

    /// @notice Returns whether an application should be queried for token balances.
    /// @dev GRG_STAKING is always queried: it is a pre-existing application that self-activates
    ///  on the first NAV write, so the active-bit may not be set yet on a fresh pool.
    /// @param packedApplications The bitmap packed active applications flags.
    /// @param appIndex The application to check.
    /// @return bool Whether the application should be queried.
    function shouldQueryApp(uint256 packedApplications, uint256 appIndex) internal pure returns (bool) {
        if (Applications(appIndex) == Applications.GRG_STAKING) return true;
        return isActiveApplication(packedApplications, appIndex);
    }
}

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

/// @notice Per-pool delegated-access state stored at a dedicated ERC-7201 slot.
/// @dev Four parallel data structures maintain two enumerable mappings:
///      selector → [delegated addresses]  (for revoke-by-selector)
///      address  → [delegated selectors]  (for revoke-by-address)
///      Both directions are kept in O(1) via position tracking (1-indexed, 0 = absent).
struct DelegationData {
    /// @dev selector → (address → 1-indexed position in selectorAddresses[selector]). 0 = not delegated.
    mapping(bytes4 selector => mapping(address delegated => uint256 position)) selectorToAddressPosition;
    /// @dev selector → ordered list of delegated addresses.
    mapping(bytes4 selector => address[] addresses) selectorAddresses;
    /// @dev address → ordered list of selectors delegated to it.
    mapping(address delegated => bytes4[] selectors) addressSelectors;
    /// @dev address → (selector → 1-indexed position in addressSelectors[address]). 0 = not present.
    mapping(address delegated => mapping(bytes4 selector => uint256 position)) addressToSelectorPosition;
}

/// @title DelegationLib - Enumerable bi-directional delegation registry.
/// @notice Library for managing granular per-selector delegated write access to pool adapters.
/// @dev All writes maintain two enumerable index structures so callers can enumerate delegations
///      in either direction without iterating unpredictably large arrays.
library DelegationLib {
    // -------------------------------------------------------------------------
    // Mutating operations
    // -------------------------------------------------------------------------

    /// @notice Grants delegated write access to `selector` for `addr`.
    /// @return added True if the pair was newly granted (false = already existed, no storage change).
    function add(DelegationData storage self, bytes4 selector, address addr) internal returns (bool added) {
        if (self.selectorToAddressPosition[selector][addr] != 0) return false;

        // Register in selector → addresses direction
        self.selectorAddresses[selector].push(addr);
        self.selectorToAddressPosition[selector][addr] = self.selectorAddresses[selector].length; // 1-indexed

        // Register in address → selectors direction
        self.addressSelectors[addr].push(selector);
        self.addressToSelectorPosition[addr][selector] = self.addressSelectors[addr].length; // 1-indexed

        return true;
    }

    /// @notice Revokes delegated write access to `selector` for `addr`.
    /// @return removed True if the pair was present and removed (false = was not delegated, no storage change).
    function remove(DelegationData storage self, bytes4 selector, address addr) internal returns (bool removed) {
        uint256 posInSelector = self.selectorToAddressPosition[selector][addr];
        if (posInSelector == 0) return false;

        // Swap-and-pop from selectorAddresses[selector]
        address[] storage addrList = self.selectorAddresses[selector];
        uint256 lastIdx = addrList.length - 1;
        uint256 removeIdx = posInSelector - 1;

        if (removeIdx != lastIdx) {
            address lastAddr = addrList[lastIdx];
            addrList[removeIdx] = lastAddr;
            self.selectorToAddressPosition[selector][lastAddr] = posInSelector;
        }
        addrList.pop();
        delete self.selectorToAddressPosition[selector][addr];

        // Swap-and-pop from addressSelectors[addr]
        uint256 posInAddr = self.addressToSelectorPosition[addr][selector];
        bytes4[] storage selList = self.addressSelectors[addr];
        uint256 lastSel = selList.length - 1;
        uint256 removeSelIdx = posInAddr - 1;

        if (removeSelIdx != lastSel) {
            bytes4 lastSelector = selList[lastSel];
            selList[removeSelIdx] = lastSelector;
            self.addressToSelectorPosition[addr][lastSelector] = posInAddr;
        }
        selList.pop();
        delete self.addressToSelectorPosition[addr][selector];

        return true;
    }

    /// @notice Revokes all delegations previously granted to `addr` (e.g. compromised wallet).
    /// @dev Iterates the (short) list of selectors for addr and cleans up both directions.
    function removeAllByAddress(DelegationData storage self, address addr) internal {
        bytes4[] storage selectors = self.addressSelectors[addr];
        uint256 len = selectors.length;

        for (uint256 i = 0; i < len; ++i) {
            bytes4 sel = selectors[i];

            // Remove addr from selectorAddresses[sel] via swap-and-pop
            uint256 posInSelector = self.selectorToAddressPosition[sel][addr];
            address[] storage addrList = self.selectorAddresses[sel];
            uint256 lastIdx = addrList.length - 1;
            uint256 removeIdx = posInSelector - 1;

            if (removeIdx != lastIdx) {
                address lastAddr = addrList[lastIdx];
                addrList[removeIdx] = lastAddr;
                self.selectorToAddressPosition[sel][lastAddr] = posInSelector;
            }
            addrList.pop();
            delete self.selectorToAddressPosition[sel][addr];
            delete self.addressToSelectorPosition[addr][sel];
        }

        delete self.addressSelectors[addr];
    }

    /// @notice Revokes all delegations for `selector` (e.g. adapter being replaced by governance).
    /// @dev Iterates the (short) list of addresses for selector and cleans up both directions.
    function removeAllBySelector(DelegationData storage self, bytes4 selector) internal {
        address[] storage addrs = self.selectorAddresses[selector];
        uint256 len = addrs.length;

        for (uint256 i = 0; i < len; ++i) {
            address addr = addrs[i];

            // Remove selector from addressSelectors[addr] via swap-and-pop
            uint256 posInAddr = self.addressToSelectorPosition[addr][selector];
            bytes4[] storage selList = self.addressSelectors[addr];
            uint256 lastSel = selList.length - 1;
            uint256 removeSel = posInAddr - 1;

            if (removeSel != lastSel) {
                bytes4 lastSelector = selList[lastSel];
                selList[removeSel] = lastSelector;
                self.addressToSelectorPosition[addr][lastSelector] = posInAddr;
            }
            selList.pop();
            delete self.addressToSelectorPosition[addr][selector];
            delete self.selectorToAddressPosition[selector][addr];
        }

        delete self.selectorAddresses[selector];
    }

    // -------------------------------------------------------------------------
    // View helpers
    // -------------------------------------------------------------------------

    /// @notice Returns all addresses currently delegated for `selector`.
    function getAddresses(DelegationData storage self, bytes4 selector) internal view returns (address[] memory) {
        return self.selectorAddresses[selector];
    }

    /// @notice Returns all selectors currently delegated to `addr`.
    function getSelectors(DelegationData storage self, address addr) internal view returns (bytes4[] memory) {
        return self.addressSelectors[addr];
    }
}

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

import {IEOracle} from "../extensions/adapters/interfaces/IEOracle.sol";
import {ISmartPoolEvents} from "../interfaces/v4/pool/ISmartPoolEvents.sol";

struct AddressSet {
    // List of stored addresses
    address[] addresses;
    // Mapping of address to position.
    // Position 0 means an address has never been added before.
    mapping(address token => uint256 position) positions;
}

/// @notice Pool initialization parameters.
/// @dev This struct is not visible externally and used to store/read pool init params.
/// @param name String of the pool name (max 32 characters).
/// @param symbol Bytes8 of the pool symbol (from 3 to 5 characters).
/// @param decimals Uint8 decimals.
/// @param owner Address of the pool operator.
/// @param unlocked Boolean the pool is locked for reentrancy check.
/// @param baseToken Address of the base token of the pool (0 for base currency).
struct Pool {
    string name;
    bytes8 symbol;
    uint8 decimals;
    address owner;
    bool unlocked;
    address baseToken;
}

/// @notice Adapted from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/structs/EnumerableSet.sol
library EnumerableSet {
    error AddressListExceedsMaxLength();
    error TokenPriceFeedDoesNotExist(address token);

    // limit size of array to prevent DOS to nav estimates
    uint256 internal constant _MAX_UNIQUE_VALUES = type(uint8).max / 2; // max 128 values

    // flag for removed address
    uint256 private constant REMOVED_ADDRESS_FLAG = type(uint256).max;

    /// @notice Like addUnique but also returns whether the token was active before this call.
    /// @dev Reads set.positions[token] exactly once, saving one SLOAD vs separate isActive + addUnique.
    ///  Returns true for base token (always considered active, never stored in the set).
    function addAndCheckWasActive(
        AddressSet storage set,
        IEOracle eOracle,
        address token,
        address baseToken
    ) internal returns (bool wasActive) {
        if (token == baseToken) return true;

        uint256 position = set.positions[token]; // single SLOAD
        wasActive = (position != 0 && position != REMOVED_ADDRESS_FLAG);

        if (!wasActive) {
            require(set.addresses.length < _MAX_UNIQUE_VALUES, AddressListExceedsMaxLength());
            require(eOracle.hasPriceFeed(token), TokenPriceFeedDoesNotExist(token));
            set.addresses.push(token);
            set.positions[token] = set.addresses.length;
            emit ISmartPoolEvents.TokenStatusChanged(token, true);
        }
    }

    /// @notice Base token is never pushed to active tokens, as already stored.
    /// @dev Skips and returns false for base token, which is already in storage.
    function addUnique(AddressSet storage set, IEOracle eOracle, address token, address baseToken) internal {
        if (token != baseToken) {
            if (set.positions[token] == 0 || set.positions[token] == REMOVED_ADDRESS_FLAG) {
                require(set.addresses.length < _MAX_UNIQUE_VALUES, AddressListExceedsMaxLength());

                // perform a staticcall to the oracle extension and assert token has a price feed
                require(eOracle.hasPriceFeed(token), TokenPriceFeedDoesNotExist(token));

                // update storage
                set.addresses.push(token);
                set.positions[token] = set.addresses.length;

                // emit event for token activation
                emit ISmartPoolEvents.TokenStatusChanged(token, true);
            }
        }
    }

    function remove(AddressSet storage set, address token) internal {
        uint256 position = set.positions[token];

        if (position != 0 && position != REMOVED_ADDRESS_FLAG) {
            // Copy last element at position and pop last element
            uint256 addressIndex = position - 1;
            uint256 lastIndex = set.addresses.length - 1;

            if (addressIndex != lastIndex) {
                address lastAddress = set.addresses[lastIndex];

                // Move the lastToken to the index where the token to delete is
                set.addresses[addressIndex] = lastAddress;
                // Update the tracked position of the lastToken (that was just moved)
                set.positions[lastAddress] = position;
            }

            // Delete the slot where the moved token was stored
            set.addresses.pop();

            // Delete the tracked position for the deleted slot without clearing storage
            set.positions[token] = REMOVED_ADDRESS_FLAG;

            // emit event for token deactivation
            emit ISmartPoolEvents.TokenStatusChanged(token, false);
            return;
        }
    }

    function isActive(AddressSet storage set, address token) internal view returns (bool) {
        uint256 position = set.positions[token];
        return (position != 0 && position != REMOVED_ADDRESS_FLAG);
    }
}

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

import {SafeCast} from "@openzeppelin-legacy/contracts/utils/math/SafeCast.sol";
import {ISmartPoolState} from "../interfaces/v4/pool/ISmartPoolState.sol";
import {StorageLib} from "./StorageLib.sol";
import {VirtualStorageLib} from "./VirtualStorageLib.sol";
import {IEOracle} from "../extensions/adapters/interfaces/IEOracle.sol";

/// @title NavImpactLib - Library for validating NAV impact tolerance
/// @notice Provides percentage-based NAV impact validation for cross-chain transfers
/// @dev Used by both AIntents (source) and ECrosschain (destination) for consistent validation
/// @author Gabriele Rigo - <[email protected]>
library NavImpactLib {
    using SafeCast for uint256;
    using SafeCast for int256;

    error EffectiveSupplyTooLow();

    /// @notice Thrown when transfer amount exceeds maximum allowed NAV impact
    /// @dev Impact is calculated as (transferValue * 10000) / totalAssetsValue in basis points
    error NavImpactTooHigh();

    /// @notice Minimum ratio of effective supply to total supply (1/8 = 12.5%)
    /// @dev When virtual supply is negative, effective supply must be at least totalSupply / MINIMUM_SUPPLY_RATIO
    uint256 internal constant MINIMUM_SUPPLY_RATIO = 8;

    /// @notice Validates that transfer amount doesn't exceed NAV impact tolerance
    /// @dev Calculates percentage impact: (transferValue * 10000) / totalAssetsValue vs toleranceBps
    /// @param token Token being transferred
    /// @param amount Amount being transferred
    /// @param toleranceBps Maximum allowed NAV impact in basis points (e.g., 1000 = 10%)
    function validateNavImpact(address token, uint256 amount, uint256 toleranceBps) internal view {
        // Get current pool state
        ISmartPoolState.PoolTokens memory poolTokens = ISmartPoolState(address(this)).getPoolTokens();
        uint8 poolDecimals = StorageLib.pool().decimals;
        address baseToken = StorageLib.pool().baseToken;

        // Calculate effective supply using signed arithmetic (VS can be negative)
        int256 virtualSupply = VirtualStorageLib.getVirtualSupply();
        int256 effectiveSupply = int256(poolTokens.totalSupply) + virtualSupply;
        if (effectiveSupply <= 0) {
            return; // No effective supply, allow any transfer
        }

        uint256 totalAssetsValue = (poolTokens.unitaryValue * uint256(effectiveSupply)) / (10 ** poolDecimals);

        // For empty pools (all supply burnt on all chains), allow any transfer. Handles edge case of receiving first tokens on a chain
        if (totalAssetsValue == 0) {
            return;
        }

        // Convert transfer amount to base token value for percentage calculation
        int256 transferValueInBase;
        if (token == baseToken) {
            transferValueInBase = amount.toInt256();
        } else {
            // Use convertTokenAmount for non-base tokens
            transferValueInBase = IEOracle(address(this)).convertTokenAmount(token, amount.toInt256(), baseToken);
        }

        // Calculate percentage impact in basis points: (transferValue * 10000) / totalAssetsValue
        uint256 transferValue = transferValueInBase.toUint256();
        uint256 impactBps = (transferValue * 10000) / totalAssetsValue;

        // Validate impact is within tolerance
        require(impactBps <= toleranceBps, NavImpactTooHigh());
    }

    /// @notice Validates that effective supply meets minimum threshold when virtual supply is negative
    function validateSupply(uint256 totalSupply, int256 virtualSupply) internal pure {
        if (virtualSupply < 0) {
            if (uint256(-virtualSupply) * MINIMUM_SUPPLY_RATIO > totalSupply * (MINIMUM_SUPPLY_RATIO - 1)) {
                revert EffectiveSupplyTooLow();
            }
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/ReentrancyGuardTransient.sol)

pragma solidity ^0.8.24;

import {TransientSlot} from "./TransientSlot.sol";

/**
 * @dev Variant of {ReentrancyGuard} that uses transient storage.
 *
 * NOTE: This variant only works on networks where EIP-1153 is available.
 *
 * _Available since v5.1._
 */
abstract contract ReentrancyGuardTransient {
    using TransientSlot for *;

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ReentrancyGuard")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant REENTRANCY_GUARD_STORAGE =
        0x9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00;

    /**
     * @dev Unauthorized reentrant call.
     */
    error ReentrancyGuardReentrantCall();

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, REENTRANCY_GUARD_STORAGE.asBoolean().tload() will be false
        if (_reentrancyGuardEntered()) {
            revert ReentrancyGuardReentrantCall();
        }

        // Any calls to nonReentrant after this point will fail
        REENTRANCY_GUARD_STORAGE.asBoolean().tstore(true);
    }

    function _nonReentrantAfter() private {
        REENTRANCY_GUARD_STORAGE.asBoolean().tstore(false);
    }

    /**
     * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
     * `nonReentrant` function in the call stack.
     */
    function _reentrancyGuardEntered() internal view returns (bool) {
        return REENTRANCY_GUARD_STORAGE.asBoolean().tload();
    }
}

// SPDX-License-Identifier: Apache3.0-or-later
pragma solidity >=0.8.28;

import {IERC20} from "../interfaces/IERC20.sol";

type Currency is address;

/// @title SafeTransferLib
/// @dev This library allows for safe transfer of tokens without using assembly
library SafeTransferLib {
    error ApprovalFailed(address token);
    error NativeTransferFailed();
    error TokenTransferFailed();
    error TokenTransferFromFailed();
    error ApprovalTargetIsNotContract(address token);

    function safeTransferNative(address to, uint256 amount) internal {
        (bool success, ) = to.call{gas: 2300, value: amount}("");
        require(success, NativeTransferFailed());
    }

    function safeTransfer(address token, address to, uint256 amount) internal {
        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory data) = token.call(abi.encodeCall(IERC20.transfer, (to, amount)));
        require(success && (data.length == 0 || abi.decode(data, (bool))), TokenTransferFailed());
    }

    function safeTransferFrom(address token, address from, address to, uint256 amount) internal {
        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory data) = token.call(abi.encodeCall(IERC20.transferFrom, (from, to, amount)));
        require(success && (data.length == 0 || abi.decode(data, (bool))), TokenTransferFromFailed());
    }

    /// @dev Allows approving all ERC20 tokens, forcing approvals when needed.
    function safeApprove(address token, address spender, uint256 amount) internal {
        // token address sanity check
        bool isContract = token.code.length > 0;
        require(isContract, ApprovalTargetIsNotContract(token));
        (bool success, bytes memory data) = token.call(abi.encodeCall(IERC20.approve, (spender, amount)));

        if (!success || (data.length != 0 && !abi.decode(data, (bool)))) {
            // force approval
            (success, data) = token.call(abi.encodeCall(IERC20.approve, (spender, 0)));
            (success, data) = token.call(abi.encodeCall(IERC20.approve, (spender, amount)));

            require(success && ((data.length == 0 && isContract) || abi.decode(data, (bool))), ApprovalFailed(token));
        }
    }

    function isAddressZero(address target) internal pure returns (bool) {
        return target == address(0);
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/SlotDerivation.sol)
// This file was procedurally generated from scripts/generate/templates/SlotDerivation.js.

pragma solidity ^0.8.20;

/**
 * @dev Library for computing storage (and transient storage) locations from namespaces and deriving slots
 * corresponding to standard patterns. The derivation method for array and mapping matches the storage layout used by
 * the solidity language / compiler.
 *
 * See https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays[Solidity docs for mappings and dynamic arrays.].
 *
 * Example usage:
 * ```solidity
 * contract Example {
 *     // Add the library methods
 *     using StorageSlot for bytes32;
 *     using SlotDerivation for bytes32;
 *
 *     // Declare a namespace
 *     string private constant _NAMESPACE = "<namespace>" // eg. OpenZeppelin.Slot
 *
 *     function setValueInNamespace(uint256 key, address newValue) internal {
 *         _NAMESPACE.erc7201Slot().deriveMapping(key).getAddressSlot().value = newValue;
 *     }
 *
 *     function getValueInNamespace(uint256 key) internal view returns (address) {
 *         return _NAMESPACE.erc7201Slot().deriveMapping(key).getAddressSlot().value;
 *     }
 * }
 * ```
 *
 * TIP: Consider using this library along with {StorageSlot}.
 *
 * NOTE: This library provides a way to manipulate storage locations in a non-standard way. Tooling for checking
 * upgrade safety will ignore the slots accessed through this library.
 *
 * _Available since v5.1._
 */
library SlotDerivation {
    /**
     * @dev Derive an ERC-7201 slot from a string (namespace).
     */
    function erc7201Slot(string memory namespace) internal pure returns (bytes32 slot) {
        assembly ("memory-safe") {
            mstore(0x00, sub(keccak256(add(namespace, 0x20), mload(namespace)), 1))
            slot := and(keccak256(0x00, 0x20), not(0xff))
        }
    }

    /**
     * @dev Add an offset to a slot to get the n-th element of a structure or an array.
     */
    function offset(bytes32 slot, uint256 pos) internal pure returns (bytes32 result) {
        unchecked {
            return bytes32(uint256(slot) + pos);
        }
    }

    /**
     * @dev Derive the location of the first element in an array from the slot where the length is stored.
     */
    function deriveArray(bytes32 slot) internal pure returns (bytes32 result) {
        assembly ("memory-safe") {
            mstore(0x00, slot)
            result := keccak256(0x00, 0x20)
        }
    }

    /**
     * @dev Derive the location of a mapping element from the key.
     */
    function deriveMapping(bytes32 slot, address key) internal pure returns (bytes32 result) {
        assembly ("memory-safe") {
            mstore(0x00, and(key, shr(96, not(0))))
            mstore(0x20, slot)
            result := keccak256(0x00, 0x40)
        }
    }

    /**
     * @dev Derive the location of a mapping element from the key.
     */
    function deriveMapping(bytes32 slot, bool key) internal pure returns (bytes32 result) {
        assembly ("memory-safe") {
            mstore(0x00, iszero(iszero(key)))
            mstore(0x20, slot)
            result := keccak256(0x00, 0x40)
        }
    }

    /**
     * @dev Derive the location of a mapping element from the key.
     */
    function deriveMapping(bytes32 slot, bytes32 key) internal pure returns (bytes32 result) {
        assembly ("memory-safe") {
            mstore(0x00, key)
            mstore(0x20, slot)
            result := keccak256(0x00, 0x40)
        }
    }

    /**
     * @dev Derive the location of a mapping element from the key.
     */
    function deriveMapping(bytes32 slot, uint256 key) internal pure returns (bytes32 result) {
        assembly ("memory-safe") {
            mstore(0x00, key)
            mstore(0x20, slot)
            result := keccak256(0x00, 0x40)
        }
    }

    /**
     * @dev Derive the location of a mapping element from the key.
     */
    function deriveMapping(bytes32 slot, int256 key) internal pure returns (bytes32 result) {
        assembly ("memory-safe") {
            mstore(0x00, key)
            mstore(0x20, slot)
            result := keccak256(0x00, 0x40)
        }
    }

    /**
     * @dev Derive the location of a mapping element from the key.
     */
    function deriveMapping(bytes32 slot, string memory key) internal pure returns (bytes32 result) {
        assembly ("memory-safe") {
            let length := mload(key)
            let begin := add(key, 0x20)
            let end := add(begin, length)
            let cache := mload(end)
            mstore(end, slot)
            result := keccak256(begin, add(length, 0x20))
            mstore(end, cache)
        }
    }

    /**
     * @dev Derive the location of a mapping element from the key.
     */
    function deriveMapping(bytes32 slot, bytes memory key) internal pure returns (bytes32 result) {
        assembly ("memory-safe") {
            let length := mload(key)
            let begin := add(key, 0x20)
            let end := add(begin, length)
            let cache := mload(end)
            mstore(end, slot)
            result := keccak256(begin, add(length, 0x20))
            mstore(end, cache)
        }
    }
}

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

import {TokenIdsSlot} from "../types/Applications.sol";
import {ApplicationsSlot} from "./ApplicationsLib.sol";
import {DelegationData} from "./DelegationLib.sol";
import {AddressSet, EnumerableSet, Pool} from "./EnumerableSet.sol";

/// @notice A library for extensions to access proxy pre-assigned storage slots.
library StorageLib {
    using EnumerableSet for AddressSet;

    /// @notice persistent storage slots, used to read from proxy storage without having to update implementation
    bytes32 public constant APPLICATIONS_SLOT = 0xdc487a67cca3fd0341a90d1b8834103014d2a61e6a212e57883f8680b8f9c831;
    bytes32 public constant DELEGATION_SLOT = 0x1de728329845ca9693f4e251833e4fd20a461e4f39179bee6e55171aedb6dc19;
    bytes32 public constant POOL_INIT_SLOT = 0xe48b9bb119adfc3bccddcc581484cc6725fe8d292ebfcec7d67b1f93138d8bd8;
    bytes32 public constant POOL_TOKENS_SLOT = 0xf46fb7ff9ff9a406787c810524417c818e45ab2f1997f38c2555c845d23bb9f6;
    bytes32 public constant TOKEN_REGISTRY_SLOT = 0x3dcde6752c7421366e48f002bbf8d6493462e0e43af349bebb99f0470a12300d;
    bytes32 public constant UNIV4_TOKEN_IDS_SLOT = 0xd87266b00c1e82928c0b0200ad56e2ee648a35d4e9b273d2ac9533471e3b5d3c;

    function pool() internal pure returns (Pool storage s) {
        assembly {
            s.slot := POOL_INIT_SLOT
        }
    }

    function delegation() internal pure returns (DelegationData storage s) {
        assembly {
            s.slot := DELEGATION_SLOT
        }
    }

    function activeTokensSet() internal pure returns (AddressSet storage s) {
        assembly {
            s.slot := TOKEN_REGISTRY_SLOT
        }
    }

    function uniV4TokenIdsSlot() internal pure returns (TokenIdsSlot storage s) {
        assembly {
            s.slot := UNIV4_TOKEN_IDS_SLOT
        }
    }

    function activeApplications() internal pure returns (ApplicationsSlot storage s) {
        assembly {
            s.slot := APPLICATIONS_SLOT
        }
    }

    /// @notice Checks if a token is owned by the pool (base token or active token)
    /// @param token Token address to check
    /// @return True if token is base token or in active tokens set
    function isOwnedToken(address token) internal view returns (bool) {
        address baseToken = pool().baseToken;

        // Base token is always owned
        if (token == baseToken) {
            return true;
        }

        // Check if token is in active tokens set
        return activeTokensSet().isActive(token);
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/TransientSlot.sol)
// This file was procedurally generated from scripts/generate/templates/TransientSlot.js.

pragma solidity ^0.8.24;

/**
 * @dev Library for reading and writing value-types to specific transient storage slots.
 *
 * Transient slots are often used to store temporary values that are removed after the current transaction.
 * This library helps with reading and writing to such slots without the need for inline assembly.
 *
 *  * Example reading and writing values using transient storage:
 * ```solidity
 * contract Lock {
 *     using TransientSlot for *;
 *
 *     // Define the slot. Alternatively, use the SlotDerivation library to derive the slot.
 *     bytes32 internal constant _LOCK_SLOT = 0xf4678858b2b588224636b8522b729e7722d32fc491da849ed75b3fdf3c84f542;
 *
 *     modifier locked() {
 *         require(!_LOCK_SLOT.asBoolean().tload());
 *
 *         _LOCK_SLOT.asBoolean().tstore(true);
 *         _;
 *         _LOCK_SLOT.asBoolean().tstore(false);
 *     }
 * }
 * ```
 *
 * TIP: Consider using this library along with {SlotDerivation}.
 */
library TransientSlot {
    /**
     * @dev UDVT that represent a slot holding a address.
     */
    type AddressSlot is bytes32;

    /**
     * @dev Cast an arbitrary slot to a AddressSlot.
     */
    function asAddress(bytes32 slot) internal pure returns (AddressSlot) {
        return AddressSlot.wrap(slot);
    }

    /**
     * @dev UDVT that represent a slot holding a bool.
     */
    type BooleanSlot is bytes32;

    /**
     * @dev Cast an arbitrary slot to a BooleanSlot.
     */
    function asBoolean(bytes32 slot) internal pure returns (BooleanSlot) {
        return BooleanSlot.wrap(slot);
    }

    /**
     * @dev UDVT that represent a slot holding a bytes32.
     */
    type Bytes32Slot is bytes32;

    /**
     * @dev Cast an arbitrary slot to a Bytes32Slot.
     */
    function asBytes32(bytes32 slot) internal pure returns (Bytes32Slot) {
        return Bytes32Slot.wrap(slot);
    }

    /**
     * @dev UDVT that represent a slot holding a uint256.
     */
    type Uint256Slot is bytes32;

    /**
     * @dev Cast an arbitrary slot to a Uint256Slot.
     */
    function asUint256(bytes32 slot) internal pure returns (Uint256Slot) {
        return Uint256Slot.wrap(slot);
    }

    /**
     * @dev UDVT that represent a slot holding a int256.
     */
    type Int256Slot is bytes32;

    /**
     * @dev Cast an arbitrary slot to a Int256Slot.
     */
    function asInt256(bytes32 slot) internal pure returns (Int256Slot) {
        return Int256Slot.wrap(slot);
    }

    /**
     * @dev Load the value held at location `slot` in transient storage.
     */
    function tload(AddressSlot slot) internal view returns (address value) {
        assembly ("memory-safe") {
            value := tload(slot)
        }
    }

    /**
     * @dev Store `value` at location `slot` in transient storage.
     */
    function tstore(AddressSlot slot, address value) internal {
        assembly ("memory-safe") {
            tstore(slot, value)
        }
    }

    /**
     * @dev Load the value held at location `slot` in transient storage.
     */
    function tload(BooleanSlot slot) internal view returns (bool value) {
        assembly ("memory-safe") {
            value := tload(slot)
        }
    }

    /**
     * @dev Store `value` at location `slot` in transient storage.
     */
    function tstore(BooleanSlot slot, bool value) internal {
        assembly ("memory-safe") {
            tstore(slot, value)
        }
    }

    /**
     * @dev Load the value held at location `slot` in transient storage.
     */
    function tload(Bytes32Slot slot) internal view returns (bytes32 value) {
        assembly ("memory-safe") {
            value := tload(slot)
        }
    }

    /**
     * @dev Store `value` at location `slot` in transient storage.
     */
    function tstore(Bytes32Slot slot, bytes32 value) internal {
        assembly ("memory-safe") {
            tstore(slot, value)
        }
    }

    /**
     * @dev Load the value held at location `slot` in transient storage.
     */
    function tload(Uint256Slot slot) internal view returns (uint256 value) {
        assembly ("memory-safe") {
            value := tload(slot)
        }
    }

    /**
     * @dev Store `value` at location `slot` in transient storage.
     */
    function tstore(Uint256Slot slot, uint256 value) internal {
        assembly ("memory-safe") {
            tstore(slot, value)
        }
    }

    /**
     * @dev Load the value held at location `slot` in transient storage.
     */
    function tload(Int256Slot slot) internal view returns (int256 value) {
        assembly ("memory-safe") {
            value := tload(slot)
        }
    }

    /**
     * @dev Store `value` at location `slot` in transient storage.
     */
    function tstore(Int256Slot slot, int256 value) internal {
        assembly ("memory-safe") {
            tstore(slot, value)
        }
    }
}

// SPDX-License-Identifier: Apache-2.0-or-later
pragma solidity ^0.8.24;

import {SlotDerivation} from "./SlotDerivation.sol";
import {TransientSlot} from "./TransientSlot.sol";

type Int256 is bytes32;

library TransientStorage {
    using TransientSlot for *;
    using SlotDerivation for bytes32;

    bytes32 internal constant _TRANSIENT_BALANCE_SLOT =
        bytes32(uint256(keccak256("mixin.value.transient.balance")) - 1);

    bytes32 internal constant _TRANSIENT_TWAP_TICK_SLOT = bytes32(uint256(keccak256("transient.tick.slot")) - 1);

    // Transient storage slots for cross-chain donation tracking
    // _STORED_NAV_SLOT is used to store NAV at donation init time for proper share calculation
    bytes32 internal constant _STORED_NAV_SLOT = bytes32(uint256(keccak256("eacross.stored.nav")) - 1);
    bytes32 internal constant _STORED_ASSETS_SLOT = bytes32(uint256(keccak256("eacross.stored.assets")) - 1);
    bytes32 internal constant _TEMP_BALANCE_SLOT = bytes32(uint256(keccak256("eacross.temp.balance")) - 1);
    bytes32 internal constant _DONATION_LOCK_SLOT = bytes32(uint256(keccak256("eacross.donation.lock")) - 1);

    // Helper functions for tstore operations
    /// @notice Stores a mapping of token addresses to int256 values
    function store(Int256 slot, address token, int256 value) internal {
        Int256.unwrap(slot).deriveMapping(token).asInt256().tstore(value);
    }

    function get(Int256 slot, address token) internal view returns (int256) {
        return Int256.unwrap(slot).deriveMapping(token).asInt256().tload();
    }

    function storeBalance(address token, int256 balance) internal {
        store(Int256.wrap(_TRANSIENT_BALANCE_SLOT), token, balance);
    }

    function getBalance(address token) internal view returns (int256) {
        return get(Int256.wrap(_TRANSIENT_BALANCE_SLOT), token);
    }

    function storeTwap(address token, int24 twap) internal {
        store(Int256.wrap(_TRANSIENT_TWAP_TICK_SLOT), token, int256(twap));
    }

    function getTwap(address token) internal view returns (int24) {
        return int24(get(Int256.wrap(_TRANSIENT_TWAP_TICK_SLOT), token));
    }

    function setDonationLock(address token, uint256 balance) internal {
        bool isUnlocked = getDonationLock();
        _DONATION_LOCK_SLOT.asBoolean().tstore(!isUnlocked);
        storeTemporaryBalance(token, balance, !isUnlocked);
    }

    function getDonationLock() internal view returns (bool) {
        return _DONATION_LOCK_SLOT.asBoolean().tload();
    }

    function getTemporaryBalance(address token) internal view returns (uint256, bool) {
        bytes32 slot = _TEMP_BALANCE_SLOT.deriveMapping(token);
        return (slot.asUint256().tload(), (bytes32(uint256(slot) + 1)).asBoolean().tload());
    }

    function storeNav(uint256 nav) internal {
        _STORED_NAV_SLOT.asUint256().tstore(nav);
    }

    function storeAssets(uint256 assets) internal {
        _STORED_ASSETS_SLOT.asUint256().tstore(assets);
    }

    function storeTemporaryBalance(address token, uint256 balance, bool locked) private {
        bytes32 slot = _TEMP_BALANCE_SLOT.deriveMapping(token);
        slot.asUint256().tstore(balance);
        (bytes32(uint256(slot) + 1)).asBoolean().tstore(locked);
    }

    function getStoredNav() internal view returns (uint256) {
        return _STORED_NAV_SLOT.asUint256().tload();
    }

    function getStoredAssets() internal view returns (uint256) {
        return _STORED_ASSETS_SLOT.asUint256().tload();
    }
}

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

library VersionLib {
    // Compare versions
    function isVersionHigherOrEqual(
        string memory givenVersion,
        string memory requiredVersion
    ) internal pure returns (bool) {
        uint256[3] memory given = parseVersion(givenVersion);
        uint256[3] memory required = parseVersion(requiredVersion);

        // Compare each part
        for (uint256 i = 0; i < 3; i++) {
            if (given[i] < required[i]) {
                return false;
            }
            if (given[i] > required[i]) {
                return true;
            }
        }
        // If all parts are equal, versions are not higher
        return true;
    }

    // Convert version string to an array of numbers
    function parseVersion(string memory _version) private pure returns (uint256[3] memory versionParts) {
        bytes memory b = bytes(_version);

        // caching for gas savings
        uint256 stringLength = b.length;
        uint256 partIndex = 0;
        uint256 currentNumber = 0;

        for (uint256 i = 0; i < stringLength; i++) {
            if (b[i] == ".") {
                versionParts[partIndex] = currentNumber;
                partIndex++;
                currentNumber = 0;
            } else {
                currentNumber = currentNumber * 10 + charToUint(b[i]);
            }
        }
        versionParts[partIndex] = currentNumber;
    }

    // Helper function to convert a single character to an integer
    function charToUint(bytes1 char) private pure returns (uint256) {
        uint256 digit = uint256(uint8(char)) - 48;
        require(digit < 10, "Not a digit");
        return digit;
    }
}

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

import {IECrosschain} from "../extensions/adapters/interfaces/IECrosschain.sol";

/// @title VirtualStorageLib - Library for managing virtual supply for cross-chain operations
/// @notice Provides functions to get and update virtual supply (VS-only model)
/// @dev Uses ERC-7201 namespaced storage pattern
/// @dev VS-only model: Transfer writes negative VS on source (NAV-neutral), positive VS on destination
///      Sync has no VS impact (NAV-impacting on both chains)
library VirtualStorageLib {
    bytes32 public constant VIRTUAL_SUPPLY_SLOT = 0xc1634c3ed93b1f7aa4d725c710ac3b239c1d30894404e630b60009ee3411450f;

    struct VirtualSupply {
        int256 supply;
    }

    function virtualSupply() internal pure returns (VirtualSupply storage s) {
        assembly {
            s.slot := VIRTUAL_SUPPLY_SLOT
        }
    }

    function updateVirtualSupply(int256 delta) internal {
        virtualSupply().supply += delta;
        emit IECrosschain.VirtualSupplyUpdated(delta, virtualSupply().supply);
    }

    function getVirtualSupply() internal view returns (int256) {
        return virtualSupply().supply;
    }
}

File 44 of 48 : Applications.sol
// SPDX-License-Identifier: Apache-2.0-or-later
pragma solidity ^0.8.0;

/// @notice Supported Applications.
/// @dev Preserve order when adding new applications, last one is the counter.
enum Applications {
    GRG_STAKING,
    UNIV4_LIQUIDITY,
    GMX_V2_POSITIONS,
    // append new applications here, up to a total of 255 as a theoretical maximum
    COUNT
}

struct TokenIdsSlot {
    uint256[] tokenIds;
    mapping(uint256 tokenId => uint256 index) positions;
}

File 45 of 48 : Crosschain.sol
// SPDX-License-Identifier: Apache-2.0-or-later
pragma solidity ^0.8.0;

enum OpType {
    Transfer,
    Sync,
    Unknown
}

struct SourceMessageParams {
    OpType opType;
    uint256 navTolerance;
    uint256 sourceNativeAmount;
    bool shouldUnwrapOnDestination;
}

struct DestinationMessageParams {
    OpType opType;
    bool shouldUnwrapNative;
}

/*//////////////////////////////////////////////////////////////
                    ACROSS MULTICALL HANDLER TYPES
//////////////////////////////////////////////////////////////*/

/// @notice Single call to be executed by Across MulticallHandler
/// @dev Matches Across protocol Call struct exactly
struct Call {
    address target; // Contract to call
    bytes callData; // Encoded function call data
    uint256 value; // ETH value to send with the call
}

/// @notice Complete instructions for MulticallHandler execution
/// @dev Matches Across protocol Instructions struct exactly
struct Instructions {
    Call[] calls; // Array of calls to execute
    address fallbackRecipient; // Where tokens go if calls fail (address(0) = revert on failure)
}

File 46 of 48 : Delegation.sol
// SPDX-License-Identifier: Apache 2.0-or-later
pragma solidity ^0.8.0;

/// @notice Encodes a single delegation add/remove operation.
/// @param delegated Address to grant or revoke access for.
/// @param selector Function selector to grant or revoke access to.
/// @param isDelegated True to grant, false to revoke.
struct Delegation {
    address delegated;
    bytes4 selector;
    bool isDelegated;
}

File 47 of 48 : ExternalApp.sol
// SPDX-License-Identifier: Apache-2.0-or-later
pragma solidity ^0.8.0;

struct AppTokenBalance {
    address token;
    int256 amount;
}

struct ExternalApp {
    AppTokenBalance[] balances;
    uint256 appType; // stored as a uint256 to facilityte supporting new apps
}

File 48 of 48 : NavComponents.sol
// SPDX-License-Identifier: Apache-2.0-or-later
pragma solidity ^0.8.0;

struct NavComponents {
    uint256 unitaryValue;
    uint256 totalSupply;
    address baseToken;
    uint8 decimals;
    uint256 netTotalValue; // Total pool value in base token units
    uint256 netTotalLiabilities; // Positive only when netTotalValue is negative
}

struct NetAssetsValue {
    uint256 unitaryValue;
    uint256 netTotalValue;
    uint256 netTotalLiabilities;
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "evmVersion": "cancun",
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "metadata": {
    "useLiteralContent": true
  }
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"authority","type":"address"},{"internalType":"address","name":"extensionsMap","type":"address"},{"internalType":"address","name":"tokenJar","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AddressListExceedsMaxLength","type":"error"},{"inputs":[],"name":"ApplicationIndexBitmaskRange","type":"error"},{"inputs":[],"name":"BaseTokenBalance","type":"error"},{"inputs":[],"name":"BaseTokenDecimals","type":"error"},{"inputs":[],"name":"BaseTokenPriceFeedError","type":"error"},{"inputs":[],"name":"EffectiveSupplyTooLow","type":"error"},{"inputs":[],"name":"InvalidAuthorityInput","type":"error"},{"inputs":[],"name":"InvalidExtensionsMapInput","type":"error"},{"inputs":[],"name":"InvalidOperator","type":"error"},{"inputs":[],"name":"NativeTransferFailed","type":"error"},{"inputs":[],"name":"OwnerActionInputIsSameAsCurrent","type":"error"},{"inputs":[],"name":"PoolAlreadyInitialized","type":"error"},{"inputs":[{"internalType":"uint16","name":"minimumOrderDivisor","type":"uint16"}],"name":"PoolAmountSmallerThanMinimum","type":"error"},{"inputs":[],"name":"PoolBurnNotEnough","type":"error"},{"inputs":[],"name":"PoolBurnNullAmount","type":"error"},{"inputs":[],"name":"PoolBurnOutputAmount","type":"error"},{"inputs":[],"name":"PoolCallerIsNotOwner","type":"error"},{"inputs":[],"name":"PoolCallerNotWhitelisted","type":"error"},{"inputs":[{"internalType":"uint16","name":"maxFee","type":"uint16"}],"name":"PoolFeeBiggerThanMax","type":"error"},{"inputs":[],"name":"PoolImplementationDirectCallNotAllowed","type":"error"},{"inputs":[],"name":"PoolInputIsNotContract","type":"error"},{"inputs":[{"internalType":"uint48","name":"minimum","type":"uint48"},{"internalType":"uint48","name":"maximum","type":"uint48"}],"name":"PoolLockupPeriodInvalid","type":"error"},{"inputs":[],"name":"PoolMethodNotAllowed","type":"error"},{"inputs":[],"name":"PoolMinimumPeriodNotEnough","type":"error"},{"inputs":[],"name":"PoolMintAmountIn","type":"error"},{"inputs":[],"name":"PoolMintInvalidRecipient","type":"error"},{"inputs":[],"name":"PoolMintOutputAmount","type":"error"},{"inputs":[],"name":"PoolMintTokenNotActive","type":"error"},{"inputs":[],"name":"PoolNullOwnerInput","type":"error"},{"inputs":[{"internalType":"uint16","name":"maxSpread","type":"uint16"}],"name":"PoolSpreadInvalid","type":"error"},{"inputs":[],"name":"PoolTokenNotActive","type":"error"},{"inputs":[],"name":"PoolVersionNotSupported","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"SafeCastOverflowedUintToInt","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"TokenPriceFeedDoesNotExist","type":"error"},{"inputs":[],"name":"TokenTransferFailed","type":"error"},{"inputs":[],"name":"TokenTransferFromFailed","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pool","type":"address"},{"indexed":true,"internalType":"address","name":"delegated","type":"address"},{"indexed":true,"internalType":"bytes4","name":"selector","type":"bytes4"},{"indexed":false,"internalType":"bool","name":"isDelegated","type":"bool"}],"name":"DelegationUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pool","type":"address"},{"indexed":true,"internalType":"address","name":"kycProvider","type":"address"}],"name":"KycProviderSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pool","type":"address"},{"indexed":false,"internalType":"uint48","name":"minimumPeriod","type":"uint48"}],"name":"MinimumPeriodChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pool","type":"address"},{"indexed":true,"internalType":"address","name":"who","type":"address"},{"indexed":false,"internalType":"address","name":"feeCollector","type":"address"}],"name":"NewCollector","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pool","type":"address"},{"indexed":true,"internalType":"address","name":"who","type":"address"},{"indexed":false,"internalType":"uint16","name":"transactionFee","type":"uint16"}],"name":"NewFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"pool","type":"address"},{"indexed":false,"internalType":"uint256","name":"unitaryValue","type":"uint256"}],"name":"NewNav","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"old","type":"address"},{"indexed":true,"internalType":"address","name":"current","type":"address"}],"name":"NewOwner","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"holder","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"OperatorSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"group","type":"address"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"baseToken","type":"address"},{"indexed":false,"internalType":"string","name":"name","type":"string"},{"indexed":false,"internalType":"bytes8","name":"symbol","type":"bytes8"}],"name":"PoolInitialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pool","type":"address"},{"indexed":false,"internalType":"uint16","name":"spread","type":"uint16"}],"name":"SpreadChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"bool","name":"isActive","type":"bool"}],"name":"TokenStatusChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"stateMutability":"nonpayable","type":"fallback"},{"inputs":[],"name":"VERSION","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"authority","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"who","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMin","type":"uint256"}],"name":"burn","outputs":[{"internalType":"uint256","name":"netRevenue","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"internalType":"address","name":"tokenOut","type":"address"}],"name":"burnForToken","outputs":[{"internalType":"uint256","name":"netRevenue","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"feeCollector","type":"address"}],"name":"changeFeeCollector","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint48","name":"minPeriod","type":"uint48"}],"name":"changeMinPeriod","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"newSpread","type":"uint16"}],"name":"changeSpread","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAcceptedMintTokens","outputs":[{"internalType":"address[]","name":"tokens","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getActiveApplications","outputs":[{"internalType":"uint256","name":"packedApplications","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getActiveTokens","outputs":[{"components":[{"internalType":"address[]","name":"activeTokens","type":"address[]"},{"internalType":"address","name":"baseToken","type":"address"}],"internalType":"struct ISmartPoolState.ActiveTokens","name":"tokens","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"selector","type":"bytes4"}],"name":"getDelegatedAddresses","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"delegated","type":"address"}],"name":"getDelegatedSelectors","outputs":[{"internalType":"bytes4[]","name":"","type":"bytes4[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPool","outputs":[{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"},{"internalType":"uint8","name":"decimals","type":"uint8"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"baseToken","type":"address"}],"internalType":"struct ISmartPoolState.ReturnedPool","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPoolParams","outputs":[{"components":[{"internalType":"uint48","name":"minPeriod","type":"uint48"},{"internalType":"uint16","name":"spread","type":"uint16"},{"internalType":"uint16","name":"transactionFee","type":"uint16"},{"internalType":"address","name":"feeCollector","type":"address"},{"internalType":"address","name":"kycProvider","type":"address"}],"internalType":"struct ISmartPoolState.PoolParams","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPoolStorage","outputs":[{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"},{"internalType":"uint8","name":"decimals","type":"uint8"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"baseToken","type":"address"}],"internalType":"struct ISmartPoolState.ReturnedPool","name":"poolInitParams","type":"tuple"},{"components":[{"internalType":"uint48","name":"minPeriod","type":"uint48"},{"internalType":"uint16","name":"spread","type":"uint16"},{"internalType":"uint16","name":"transactionFee","type":"uint16"},{"internalType":"address","name":"feeCollector","type":"address"},{"internalType":"address","name":"kycProvider","type":"address"}],"internalType":"struct ISmartPoolState.PoolParams","name":"poolVariables","type":"tuple"},{"components":[{"internalType":"uint256","name":"unitaryValue","type":"uint256"},{"internalType":"uint256","name":"totalSupply","type":"uint256"}],"internalType":"struct ISmartPoolState.PoolTokens","name":"poolTokensInfo","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPoolTokens","outputs":[{"components":[{"internalType":"uint256","name":"unitaryValue","type":"uint256"},{"internalType":"uint256","name":"totalSupply","type":"uint256"}],"internalType":"struct ISmartPoolState.PoolTokens","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"offset","type":"uint256"},{"internalType":"uint256","name":"length","type":"uint256"}],"name":"getStorageAt","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"slots","type":"uint256[]"}],"name":"getStorageSlotsAt","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"who","type":"address"}],"name":"getUserAccount","outputs":[{"components":[{"internalType":"uint208","name":"userBalance","type":"uint208"},{"internalType":"uint48","name":"activation","type":"uint48"}],"internalType":"struct ISmartPoolState.UserAccount","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"initializePool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"holder","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isOperator","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMin","type":"uint256"}],"name":"mint","outputs":[{"internalType":"uint256","name":"recipientAmount","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"internalType":"address","name":"tokenIn","type":"address"}],"name":"mintWithToken","outputs":[{"internalType":"uint256","name":"recipientAmount","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"purgeInactiveTokensAndApps","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"delegated","type":"address"}],"name":"revokeAllDelegations","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"selector","type":"bytes4"}],"name":"revokeAllDelegationsForSelector","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"bool","name":"isAccepted","type":"bool"}],"name":"setAcceptableMintToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"kycProvider","type":"address"}],"name":"setKycProvider","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setOperator","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"setOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"transactionFee","type":"uint16"}],"name":"setTransactionFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokenJar","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"delegated","type":"address"},{"internalType":"bytes4","name":"selector","type":"bytes4"},{"internalType":"bool","name":"isDelegated","type":"bool"}],"internalType":"struct Delegation[]","name":"delegations","type":"tuple[]"}],"name":"updateDelegation","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"updateUnitaryValue","outputs":[{"components":[{"internalType":"uint256","name":"unitaryValue","type":"uint256"},{"internalType":"uint256","name":"netTotalValue","type":"uint256"},{"internalType":"uint256","name":"netTotalLiabilities","type":"uint256"}],"internalType":"struct NetAssetsValue","name":"navParams","type":"tuple"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"wrappedNative","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]

610120604052348015610010575f5ffd5b5060405161650538038061650583398101604081905261002f91610520565b8282825f836001600160a01b03163b1161005c57604051630754288760e01b815260040160405180910390fd5b5f826001600160a01b03163b1161008657604051633c185a8d60e21b815260040160405180910390fd5b6001600160a01b038084166080523060e05282166101008190526040805163eb6d3a1160e01b8152905163eb6d3a11916004808201926020929091908290030181865afa1580156100d9573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906100fd9190610560565b6001600160a01b0390811660a0521660c0525061013d905060017fa33198d1011bad6f8d9b4a537f82cf21cfac49b1430cf1a99c11aaf4d7325fc7610594565b7fa33198d1011bad6f8d9b4a537f82cf21cfac49b1430cf1a99c11aaf4d7325fc61461016b5761016b610580565b61019660017fdc487a67cca3fd0341a90d1b8834103014d2a61e6a212e57883f8680b8f9c832610594565b7fdc487a67cca3fd0341a90d1b8834103014d2a61e6a212e57883f8680b8f9c831146101c4576101c4610580565b6101ef60017fac0ed3ab25c1c02fcfdfba47b1953f88a6f24e5a50f1076d09054047884e5351610594565b7fac0ed3ab25c1c02fcfdfba47b1953f88a6f24e5a50f1076d09054047884e53501461021d5761021d610580565b61024860017ffd7547127f88410746fb7969b9adb4f9e9d8d2436aa2d2277b1103542deb7b8f610594565b7ffd7547127f88410746fb7969b9adb4f9e9d8d2436aa2d2277b1103542deb7b8e1461027657610276610580565b61028e60015f5160206164c55f395f51905f52610594565b7fe48b9bb119adfc3bccddcc581484cc6725fe8d292ebfcec7d67b1f93138d8bd8146102bc576102bc610580565b6102e760017ff46fb7ff9ff9a406787c810524417c818e45ab2f1997f38c2555c845d23bb9f7610594565b7ff46fb7ff9ff9a406787c810524417c818e45ab2f1997f38c2555c845d23bb9f61461031557610315610580565b61032d60015f5160206164e55f395f51905f52610594565b7fe3ed9e7d534645c345f2d15f0c405f8de0227b60eb37bbeb25b26db462415dec1461035b5761035b610580565b61038660017f3dcde6752c7421366e48f002bbf8d6493462e0e43af349bebb99f0470a12300e610594565b7f3dcde6752c7421366e48f002bbf8d6493462e0e43af349bebb99f0470a12300d146103b4576103b4610580565b6103df60017fd87266b00c1e82928c0b0200ad56e2ee648a35d4e9b273d2ac9533471e3b5d3d610594565b7fd87266b00c1e82928c0b0200ad56e2ee648a35d4e9b273d2ac9533471e3b5d3c1461040d5761040d610580565b61043860017fc1634c3ed93b1f7aa4d725c710ac3b239c1d30894404e630b60009ee34114510610594565b7fc1634c3ed93b1f7aa4d725c710ac3b239c1d30894404e630b60009ee3411450f1461046657610466610580565b61049160017f1de728329845ca9693f4e251833e4fd20a461e4f39179bee6e55171aedb6dc1a610594565b7f1de728329845ca9693f4e251833e4fd20a461e4f39179bee6e55171aedb6dc19146104bf576104bf610580565b50505f5160206164c55f395f51905f528054600160481b600160e81b0319169055505f5160206164e55f395f51905f5280546001600160a01b03191660011790556105b9565b80516001600160a01b038116811461051b575f5ffd5b919050565b5f5f5f60608486031215610532575f5ffd5b61053b84610505565b925061054960208501610505565b915061055760408501610505565b90509250925092565b5f60208284031215610570575f5ffd5b61057982610505565b9392505050565b634e487b7160e01b5f52600160045260245ffd5b818103818111156105b357634e487b7160e01b5f52601160045260245ffd5b92915050565b60805160a05160c05160e05161010051615eac6106195f395f6102e101525f610c1901525f818161073401528181612da801528181612e00015281816136b301526136ff01525f610ac101525f818161039d01526109750152615eac5ff3fe608060405260043610610275575f3560e01c8063935ef3ad1161014e578063dd62ed3e116100c0578063eb6d3a1111610079578063eb6d3a1114610ab0578063ed4fd3cc14610ae3578063f1fc2f7514610b02578063f7b94b3314610b21578063fb47e01614610b34578063ffa1ad7414610bdf57610284565b8063dd62ed3e146109d7578063df0795aa146109f1578063e496fb4a14610a10578063e598a47514610a3c578063e5c2197e14610a5b578063e7d8724e14610a7a57610284565b8063b390c0ab11610112578063b390c0ab14610907578063b45940f214610926578063b6363cf214610945578063bf7e214f14610964578063d0624bed14610997578063d36ed298146109b857610284565b8063935ef3ad1461089d57806395d89b41146108b157806395f71b16146108c55780639d604df6146108e8578063a9059cbb146105c457610284565b806342377107116101e757806370a08231116101ab57806370a08231146107cd57806378b3dea4146108165780637ace8b8d1461083557806389c06568146108495780638da5cb5b1461086a5780639245290d1461087e57610284565b80634237710714610702578063490c98f514610723578063558a72971461076e5780635624b25b1461078d5780635f5817e3146107ac57610284565b8063156e29f611610239578063156e29f61461063357806318160ddd1461065457806323b872dd14610687578063250e6de0146106a9578063313ce567146106bd5780633658e24b146106e357610284565b8063026b1d5f1461057957806306fdde03146105a3578063095ea7b3146105c45780630dd56740146105f557806313af40351461061457610284565b3661028457610282610c0f565b005b34801561028f575f5ffd5b50610298610c0f565b604080515f80356001600160e01b03191660248084019190915283518084039091018152604490920183526020820180516001600160e01b031663739acf9160e11b17905291517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169161031391614e8a565b5f60405180830381855af49150503d805f811461034b576040519150601f19603f3d011682016040523d82523d5f602084013e610350565b606091505b509150505f5f8280602001905181019061036a9190614ec1565b90925090506001600160a01b0382166105375760405163c348fa1960e01b81525f356001600160e01b03191660048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063c348fa1990602401602060405180830381865afa1580156103ea573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061040e9190614ef9565b91506001600160a01b03821661043757604051630fb1627160e11b815260040160405180910390fd5b816001600160a01b0316632ea6c3f06040518163ffffffff1660e01b81526004015f60405180830381865afa92505050801561049457506040513d5f823e601f3d908101601f191682016040526104919190810190615007565b60015b156104de576040805180820190915260058152640342e322e360dc1b60208201526104bf9082610c5a565b6104dc576040516336e360fb60e01b815260040160405180910390fd5b505b5f516020615e575f395f51905f525f80356001600160e01b0319168152602091825260408082203383529092522054151580610534575061051d610d13565b60010154600160481b90046001600160a01b031633145b90505b365f5f375f6001820361055e575f5f365f865af490503d5f5f3e8061055a573d5ffd5b3d5ff35b5f5f365f865afa90503d5f5f3e80610574573d5ffd5b503d5ff35b348015610584575f5ffd5b5061058d610d37565b60405161059a91906150cf565b60405180910390f35b3480156105ae575f5ffd5b506105b7610ebf565b60405161059a91906150e1565b3480156105cf575f5ffd5b506105e56105de3660046150f3565b5f92915050565b604051901515815260200161059a565b348015610600575f5ffd5b5061028261060f36600461511d565b610f55565b34801561061f575f5ffd5b5061028261062e366004615149565b610fe9565b610646610641366004615164565b611120565b60405190815260200161059a565b34801561065f575f5ffd5b507ff46fb7ff9ff9a406787c810524417c818e45ab2f1997f38c2555c845d23bb9f754610646565b348015610692575f5ffd5b506105e56106a1366004615196565b5f9392505050565b3480156106b4575f5ffd5b50610282611147565b3480156106c8575f5ffd5b506106d16113fa565b60405160ff909116815260200161059a565b3480156106ee575f5ffd5b506102826106fd3660046151d4565b611416565b34801561070d575f5ffd5b50610716611504565b60405161059a919061524b565b34801561072e575f5ffd5b506107567f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b03909116815260200161059a565b348015610779575f5ffd5b506105e561078836600461511d565b6115b6565b348015610798575f5ffd5b506105b76107a7366004615259565b611646565b3480156107b7575f5ffd5b506107c06116bf565b60405161059a9190615279565b3480156107d8575f5ffd5b506106466107e7366004615149565b6001600160a01b03165f9081525f516020615df75f395f51905f5260205260409020546001600160d01b031690565b348015610821575f5ffd5b506106466108303660046152ec565b6116e0565b348015610840575f5ffd5b5061028261172a565b348015610854575f5ffd5b5061085d611b1b565b60405161059a9190615322565b348015610875575f5ffd5b50610756611bac565b348015610889575f5ffd5b50610282610898366004615149565b611bce565b3480156108a8575f5ffd5b50610646611cff565b3480156108bc575f5ffd5b506105b7611d08565b3480156108d0575f5ffd5b506108d9611e45565b60405161059a93929190615339565b3480156108f3575f5ffd5b50610282610902366004615372565b611ec7565b348015610912575f5ffd5b50610646610921366004615259565b611ffc565b348015610931575f5ffd5b50610282610940366004615397565b61201b565b348015610950575f5ffd5b506105e561095f3660046153b8565b612125565b34801561096f575f5ffd5b506107567f000000000000000000000000000000000000000000000000000000000000000081565b3480156109a2575f5ffd5b506109ab612171565b60405161059a9190615427565b3480156109c3575f5ffd5b506105b76109d236600461545b565b61217b565b3480156109e2575f5ffd5b506106466105de3660046153b8565b3480156109fc575f5ffd5b50610282610a0b366004615149565b61220c565b348015610a1b575f5ffd5b50610a2f610a2a366004615149565b612336565b60405161059a91906154f7565b348015610a47575f5ffd5b506109ab610a563660046151d4565b61234f565b348015610a66575f5ffd5b50610282610a75366004615543565b612368565b348015610a85575f5ffd5b50610a8e6124cc565b604080518251815260208084015190820152918101519082015260600161059a565b348015610abb575f5ffd5b506107567f000000000000000000000000000000000000000000000000000000000000000081565b348015610aee575f5ffd5b50610282610afd366004615397565b61251e565b348015610b0d575f5ffd5b50610282610b1c366004615149565b61262c565b610646610b2f3660046155b2565b612713565b348015610b3f575f5ffd5b50610bb3610b4e366004615149565b6040805180820182525f80825260209182018190526001600160a01b039390931683525f516020615df75f395f51905f528152918190208151808301909252546001600160d01b0381168252600160d01b900465ffffffffffff169181019190915290565b6040805182516001600160d01b0316815260209283015165ffffffffffff16928101929092520161059a565b348015610bea575f5ffd5b506105b7604051806040016040528060058152602001640342e322e360dc1b81525081565b6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000163003610c585760405163b08d4e7760e01b815260040160405180910390fd5b565b5f5f610c6584612781565b90505f610c7184612781565b90505f5b6003811015610d0557818160038110610c9057610c906155f9565b6020020151838260038110610ca757610ca76155f9565b60200201511015610cbd575f9350505050610d0d565b818160038110610ccf57610ccf6155f9565b6020020151838260038110610ce657610ce66155f9565b60200201511115610cfd5760019350505050610d0d565b600101610c75565b506001925050505b92915050565b7fe48b9bb119adfc3bccddcc581484cc6725fe8d292ebfcec7d67b1f93138d8bd890565b6040805160a0810182526060808252602082018190525f92820183905281018290526080810182905290610d69610d13565b6040518060c00160405290815f82018054610d839061560d565b80601f0160208091040260200160405190810160405280929190818152602001828054610daf9061560d565b8015610dfa5780601f10610dd157610100808354040283529160200191610dfa565b820191905f5260205f20905b815481529060010190602001808311610ddd57829003601f168201915b505050918352505060018201546001600160c01b031960c082901b1660208084019190915260ff600160401b830481166040808601919091526001600160a01b03600160481b850481166060870152600160e81b9094049091161515608085015260029094015490911660a0928301528251918201909252825181529192508101610e83611d08565b8152602001826040015160ff16815260200182606001516001600160a01b031681526020018260a001516001600160a01b031681525091505090565b6060610ec9610d13565b8054610ed49061560d565b80601f0160208091040260200160405190810160405280929190818152602001828054610f009061560d565b8015610f4b5780601f10610f2257610100808354040283529160200191610f4b565b820191905f5260205f20905b815481529060010190602001808311610f2e57829003601f168201915b5050505050905090565b610f5d610d13565b60010154600160481b90046001600160a01b03163314610f90576040516359dc3ad960e01b815260040160405180910390fd5b7fa33198d1011bad6f8d9b4a537f82cf21cfac49b1430cf1a99c11aaf4d7325fc68115610fdf57610fda3084610fc4610d13565b60020154849291906001600160a01b031661285d565b505050565b610fda81846129fa565b610ff1610d13565b60010154600160481b90046001600160a01b03163314611024576040516359dc3ad960e01b815260040160405180910390fd5b6001600160a01b03811661104b57604051630638d3cf60e11b815260040160405180910390fd5b611053610d13565b600101546001600160a01b03600160481b90910481169082160361108a57604051632d3ab52360e01b815260040160405180910390fd5b5f611093610d13565b60010154600160481b90046001600160a01b03169050816110b2610d13565b60010180547fffffff0000000000000000000000000000000000000000ffffffffffffffffff16600160481b6001600160a01b0393841602179055604051838216918316907f70aea8d848e8a90fb7661b227dc522eb6395c3dac71b63cb59edd5c9899b2364905f90a35050565b5f611129612b5a565b6111368484846001612bc7565b9050611140612f69565b9392505050565b303b1561116757604051637983c05160e01b815260040160405180910390fd5b5f336001600160a01b031663890357306040518163ffffffff1660e01b81526004015f60405180830381865afa1580156111a3573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526111ca9190810190615645565b6040805160c081018252825181526020808401516001600160c01b03191690820152601281830152908201516001600160a01b039081166060808401919091526001608084015283018051821660a084015251929350909116156112dd575f82606001516001600160a01b03163b11611245576112456156fd565b81606001516001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa9250505080156112a3575060408051601f3d908101601f191682019092526112a091810190615711565b60015b6112c0576040516311440b7160e11b815260040160405180910390fd5b60068160ff1610156112d4576112d46156fd565b60ff1660408201525b806112e6610d13565b815181906112f49082615775565b5060208281015160018301805460408087015160608089015160808a015160c09790971c68ffffffffffffffffff1990951694909417600160401b60ff90931692909202919091177fffff000000000000000000000000000000000000000000ffffffffffffffffff16600160481b6001600160a01b039485160260ff60e81b191617600160e81b951515959095029490941790925560a090950151600290940180546001600160a01b0319169486169490941790935585015185830151865192870151935191851694169233927f3a2fbe8bd0946e97170fefca19055b7212080a9a7415726569908565a102f574926113ee929061582f565b60405180910390a45050565b5f611403610d13565b60010154600160401b900460ff16919050565b61141e610d13565b60010154600160481b90046001600160a01b03163314611451576040516359dc3ad960e01b815260040160405180910390fd5b5f516020615e575f395f51905f525f61146a8284612f93565b9050611476828461300a565b80515f5b818110156114fd57846001600160e01b03191683828151811061149f5761149f6155f9565b60200260200101516001600160a01b0316306001600160a01b03167fca7011dbc0b320b7a80ddd4396a3a753c30a8748cd8e4128ac6be6c8fc8eef9e5f6040516114ed911515815260200190565b60405180910390a460010161147a565b5050505050565b6040805160a0810182525f808252602082018190529181018290526060810182905260808101919091526040518060a00160405280611541613213565b65ffffffffffff168152602001611556613244565b61ffff1681526020015f516020615db75f395f51905f5254600160401b900461ffff168152602001611586613271565b6001600160a01b031681526020015f516020615db75f395f51905f52600101546001600160a01b03169052919050565b335f8181527fac0ed3ab25c1c02fcfdfba47b1953f88a6f24e5a50f1076d09054047884e5350602090815260408083206001600160a01b038716808552908352818420805460ff1916871515908117909155825190815291519394909390927fceb576d9f15e4e200fdb5096d64d5dfd667e16def20c1eefd14256d8e3faa267928290030190a350600192915050565b60605f611654836020615870565b6001600160401b0381111561166b5761166b614f14565b6040519080825280601f01601f191660200182016040528015611695576020820181803683370190505b5090505f5b838110156116b7578481015460208083028401015260010161169a565b509392505050565b60408051808201909152606081525f60208201526116db6132b8565b905090565b5f6116e9612b5a565b611702825f516020615dd75f395f51905f525b9061334f565b61171f57604051637609d68760e11b815260040160405180910390fd5b61113684848461337e565b611732610d13565b60010154600160481b90046001600160a01b03163314611765576040516359dc3ad960e01b815260040160405180910390fd5b5f5f516020615dd75f395f51905f5290505f5f516020615e375f395f51905f528054604051630c872fed60e31b8152600481018290529192509060609030906364397f68906024015f604051808303815f875af19250505080156117ea57506040513d5f823e601f3d908101601f191682016040526117e79190810190615887565b60015b611842576117f6615a22565b806308c379a003611838575061180a615a3a565b80611815575061183a565b8060405162461bcd60e51b815260040161182f91906150e1565b60405180910390fd5b505b3d5f5f3e3d5ffd5b5f5b81518110156118d85781818151811061185f5761185f6155f9565b60200260200101515f0151515f14801561189b575061189b8483838151811061188a5761188a6155f9565b602002602001015160200151613731565b156118d0576118d08282815181106118b5576118b56155f9565b6020026020010151602001518661375d90919063ffffffff16565b600101611844565b508454604080516020808402820181019092528281529293505f9291879183018282801561192d57602002820191905f5260205f20905b81546001600160a01b0316815260019091019060200180831161190f575b505083519394505f9250829150505b82811015611b11575f805b8651811015611a0d575f878281518110611963576119636155f9565b6020908102919091010151515190505f5b818110156119f75787858151811061198e5761198e6155f9565b60200260200101516001600160a01b03168984815181106119b1576119b16155f9565b60200260200101515f015182815181106119cd576119cd6155f9565b60200260200101515f01516001600160a01b0316036119ef57600193506119f7565b600101611974565b508215611a045750611a0d565b50600101611947565b5080611b08575f6001600160a01b0316858381518110611a2f57611a2f6155f9565b60200260200101516001600160a01b031603611a4d57479250611ad4565b848281518110611a5f57611a5f6155f9565b60209081029190910101516040516370a0823160e01b81523060048201526001600160a01b03909116906370a0823190602401602060405180830381865afa158015611aad573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611ad19190615ab4565b92505b60018311611b0857611b08858381518110611af157611af16155f9565b60200260200101518a6129fa90919063ffffffff16565b5060010161193c565b5050505050505050565b604080518082019091525f80825260208201525f5f516020615d975f395f51905f525f015490506040518060400160405280825f03611b7a57611b5c610d13565b60010154611b7590600160401b900460ff16600a615bae565b611b7c565b825b81527ff46fb7ff9ff9a406787c810524417c818e45ab2f1997f38c2555c845d23bb9f75460209091015292915050565b5f611bb5610d13565b60010154600160481b90046001600160a01b0316919050565b611bd6610d13565b60010154600160481b90046001600160a01b03163314611c09576040516359dc3ad960e01b815260040160405180910390fd5b336001600160a01b0382161480611c255750611c258133612125565b611c425760405163ccea9e6f60e01b815260040160405180910390fd5b611c4a613271565b6001600160a01b0316816001600160a01b031603611c7b57604051632d3ab52360e01b815260040160405180910390fd5b805f516020615db75f395f51905f5280547fffff0000000000000000000000000000000000000000ffffffffffffffffffff16600160501b6001600160a01b03938416021790556040519082168152309033907fe0f5eb53a7506b986f120aada1efae98697a4d8af5d520a2b826beec9b73e31c906020015b60405180910390a350565b5f6116db61378a565b60605f611d13610d13565b6001015460c01b90505f5b60088160ff16108015611d525750818160ff1660088110611d4157611d416155f9565b1a60f81b6001600160f81b03191615155b15611d695780611d6181615bbc565b915050611d1e565b5f8160ff166001600160401b03811115611d8557611d85614f14565b6040519080825280601f01601f191660200182016040528015611daf576020820181803683370190505b5090505f91505b60088260ff16108015611dea5750828260ff1660088110611dd957611dd96155f9565b1a60f81b6001600160f81b03191615155b1561114057828260ff1660088110611e0457611e046155f9565b1a60f81b818360ff1681518110611e1d57611e1d6155f9565b60200101906001600160f81b03191690815f1a90535081611e3d81615bbc565b925050611db6565b6040805160a08082018352606080835260208084018290525f84860181905282850181905260808086018290528651948501875281855284830182905284870182905292840181905291830182905284518086019095528185528401529091611eac610d37565b611eb4611504565b611ebc611b1b565b925092509250909192565b611ecf610d13565b60010154600160481b90046001600160a01b03163314611f02576040516359dc3ad960e01b815260040160405180910390fd5b6201518065ffffffffffff821610801590611f28575062278d0065ffffffffffff821611155b6201518062278d009091611f615760405163b050fecb60e01b815265ffffffffffff92831660048201529116602482015260440161182f565b5050611f6b613213565b65ffffffffffff168165ffffffffffff1603611f9a57604051632d3ab52360e01b815260040160405180910390fd5b805f516020615db75f395f51905f52805465ffffffffffff191665ffffffffffff928316179055604051908216815230907f65abe17b1ab4c20dc5e52c5b9476b5898c128ee188e0ecc71e0b18c7498e06b5906020015b60405180910390a250565b5f612005612b5a565b6120118383600161337e565b9050610d0d612f69565b612023610d13565b60010154600160481b90046001600160a01b03163314612056576040516359dc3ad960e01b815260040160405180910390fd5b606461ffff82168110156120845760405163385988a560e01b815261ffff909116600482015260240161182f565b505f516020615db75f395f51905f525461ffff600160401b9091048116908216036120c257604051632d3ab52360e01b815260040160405180910390fd5b805f516020615db75f395f51905f52805469ffff00000000000000001916600160401b61ffff938416021790556040519082168152309033907f898ff9e005b9e94b9d353956416e9cd2a7f6c2e09cfb8357b17978aa18ba8f7490602001611cf4565b6001600160a01b039182165f9081527fac0ed3ab25c1c02fcfdfba47b1953f88a6f24e5a50f1076d09054047884e53506020908152604080832093909416825291909152205460ff1690565b60606116db6137a0565b80516060905f61218c826020615870565b6001600160401b038111156121a3576121a3614f14565b6040519080825280601f01601f1916602001820160405280156121cd576020820181803683370190505b5090505f5b828110156116b7575f8582815181106121ed576121ed6155f9565b60209081029190910181015154838202850190910152506001016121d2565b612214610d13565b60010154600160481b90046001600160a01b03163314612247576040516359dc3ad960e01b815260040160405180910390fd5b6001600160a01b0381161561227e576001600160a01b0381163b61227e57604051630a1691a360e11b815260040160405180910390fd5b7fe3ed9e7d534645c345f2d15f0c405f8de0227b60eb37bbeb25b26db462415ded546001600160a01b03908116908216036122cc57604051632d3ab52360e01b815260040160405180910390fd5b7fe3ed9e7d534645c345f2d15f0c405f8de0227b60eb37bbeb25b26db462415ded80546001600160a01b0319166001600160a01b03831690811790915560405130907f173da1498f2256abc1e3ecd01e05f37e663bbd61a3d38e44448faf6105df01f2905f90a350565b6060610d0d5f516020615e575f395f51905f528361381b565b6060610d0d5f516020615e575f395f51905f5283612f93565b612370610d13565b60010154600160481b90046001600160a01b031633146123a3576040516359dc3ad960e01b815260040160405180910390fd5b5f516020615e575f395f51905f52815f5b818110156114fd57368585838181106123cf576123cf6155f9565b90506060020190505f8160400160208101906123eb9190615bda565b61241c5761241761240260408401602085016151d4565b61240f6020850185615149565b8791906138b1565b612444565b61244461242f60408401602085016151d4565b61243c6020850185615149565b879190613b8e565b905080156124c25761245c60408301602084016151d4565b6001600160e01b0319166124736020840184615149565b6001600160a01b0316307fca7011dbc0b320b7a80ddd4396a3a753c30a8748cd8e4128ac6be6c8fc8eef9e6124ae6060870160408801615bda565b604051901515815260200160405180910390a45b50506001016123b4565b6124ed60405180606001604052805f81526020015f81526020015f81525090565b5f6124f6613c85565b60408051606081018252825181526080830151602082015260a0909201519082015292915050565b612526610d13565b60010154600160481b90046001600160a01b03163314612559576040516359dc3ad960e01b815260040160405180910390fd5b5f8161ffff1611801561257257506101f461ffff821611155b6101f49061259a576040516310a31c9b60e21b815261ffff909116600482015260240161182f565b506125a3613244565b61ffff168161ffff16036125ca57604051632d3ab52360e01b815260040160405180910390fd5b805f516020615db75f395f51905f52805467ffff0000000000001916660100000000000061ffff93841602179055604051908216815230907ff56e6a717bc79dc6398f1af61c9c98f532f4697315891b2b23cac09aca95dd2a90602001611ff1565b612634610d13565b60010154600160481b90046001600160a01b03163314612667576040516359dc3ad960e01b815260040160405180910390fd5b5f516020615e575f395f51905f525f612680828461381b565b905061268c8284613f05565b80515f5b818110156114fd578281815181106126aa576126aa6155f9565b60200260200101516001600160e01b031916856001600160a01b0316306001600160a01b03167fca7011dbc0b320b7a80ddd4396a3a753c30a8748cd8e4128ac6be6c8fc8eef9e5f604051612703911515815260200190565b60405180910390a4600101612690565b5f61271c612b5a565b612746827fa33198d1011bad6f8d9b4a537f82cf21cfac49b1430cf1a99c11aaf4d7325fc66116fc565b61276357604051633912a3f560e11b815260040160405180910390fd5b61276f85858585612bc7565b9050612779612f69565b949350505050565b612789614e1c565b815182905f80805b8381101561283a578481815181106127ab576127ab6155f9565b01602001516001600160f81b031916601760f91b036127f157818684600381106127d7576127d76155f9565b6020020152826127e681615bf5565b9350505f9150612832565b61281a858281518110612806576128066155f9565b01602001516001600160f81b0319166140f6565b61282583600a615870565b61282f9190615c0d565b91505b600101612791565b508085836003811061284e5761284e6155f9565b60200201525092949350505050565b806001600160a01b0316826001600160a01b0316146129f4576001600160a01b0382165f90815260018501602052604090205415806128b557506001600160a01b0382165f9081526001850160205260409020545f19145b156129f4576128c6600260ff615c34565b845460ff91909116116128ec57604051636e6010a560e11b815260040160405180910390fd5b6040516305bcc1d160e11b81526001600160a01b038381166004830152841690630b7983a290602401602060405180830381865afa158015612930573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906129549190615c55565b829061297f57604051631f7ddc2d60e21b81526001600160a01b03909116600482015260240161182f565b508354600180820186555f868152602080822090930180546001600160a01b0319166001600160a01b03871690811790915587548183528389018552604092839020559051918252917f0de9c5667496fd561ff9dfa59efdc1c76f7dbf3844adae3d14a3a14d563321b1910160405180910390a25b50505050565b6001600160a01b0381165f9081526001830160205260409020548015801590612a2457505f198114155b15610fda575f612a35600183615c70565b84549091505f90612a4890600190615c70565b9050808214612ad0575f855f018281548110612a6657612a666155f9565b5f9182526020909120015486546001600160a01b0390911691508190879085908110612a9457612a946155f9565b5f91825260208083209190910180546001600160a01b0319166001600160a01b0394851617905592909116815260018701909152604090208390555b8454859080612ae157612ae1615c83565b5f82815260208082205f19908401810180546001600160a01b03191690559283019093556001600160a01b038716808252600189018452604080832093909355915190815290917f0de9c5667496fd561ff9dfa59efdc1c76f7dbf3844adae3d14a3a14d563321b1910160405180910390a25050505050565b7f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f005c15612b9a57604051633ee5aeb560e01b815260040160405180910390fd5b610c5860017f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f005b90614147565b5f6001600160a01b038516612bef5760405163671565e560e01b815260040160405180910390fd5b336001600160a01b0386161480612c0b5750612c0b8533612125565b612c285760405163ccea9e6f60e01b815260040160405180910390fd5b6001600160a01b038216600114612c6a57612c6a3083612c46610d13565b600201546001600160a01b03165f516020615dd75f395f51905f525b92919061285d565b5f612c73613c85565b7fe3ed9e7d534645c345f2d15f0c405f8de0227b60eb37bbeb25b26db462415ded549091506001600160a01b03168015612d2c57604051636c46138f60e11b81526001600160a01b03888116600483015282169063d88c271e90602401602060405180830381865afa158015612ceb573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612d0f9190615c55565b612d2c5760405163a8daabcd60e01b815260040160405180910390fd5b612d358661414e565b5f612710612d41613244565b612d4f9061ffff1689615870565b612d599190615c97565b90505f196001600160a01b03861601612d7457826040015194505b6001600160a01b038516612ddc57863414612da25760405163676f83a160e01b815260040160405180910390fd5b612dd7817f00000000000000000000000000000000000000000000000000000000000000005b6001600160a01b03169061419d565b612e25565b612df16001600160a01b03861633308a614211565b612e256001600160a01b0386167f0000000000000000000000000000000000000000000000000000000000000000836142ff565b612e2f8188615c70565b965082604001516001600160a01b0316856001600160a01b031614612edd573063da0532fb86612e5e8a6143dd565b60408088015190516001600160e01b031960e086901b1681526001600160a01b0393841660048201526024810192909252919091166044820152606401602060405180830381865afa158015612eb6573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612eda9190615ab4565b96505b825160608401515f9190612ef290600a615bae565b612efc908a615870565b612f069190615c97565b9050805f516020615d975f395f51905f526001015f828254612f289190615c0d565b909155505f9050612f398a83614409565b905087811015612f5c5760405163ac06fcab60e01b815260040160405180910390fd5b9998505050505050505050565b610c585f7f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00612bc1565b6001600160e01b031981165f908152600183016020908152604091829020805483518184028101840190945280845260609392830182828015612ffd57602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311612fdf575b5050505050905092915050565b6001600160e01b031981165f9081526001830160205260408120805490915b818110156131ef575f838281548110613044576130446155f9565b5f9182526020808320909101546001600160a01b031680835260038901825260408084206001600160e01b03198a16855283528084205482855260028b019093528320805491945091929061309b90600190615c70565b90505f6130a9600185615c70565b9050818114613160575f8383815481106130c5576130c56155f9565b905f5260205f2090600891828204019190066004029054906101000a900460e01b9050808483815481106130fb576130fb6155f9565b5f91825260208083206008830401805463ffffffff60079094166004026101000a938402191660e09590951c929092029390931790556001600160a01b038816815260038d01825260408082206001600160e01b031994909416825292909152208490555b8280548061317057613170615c83565b5f828152602080822060085f1990940193840401805463ffffffff600460078716026101000a0219169055919092556001600160a01b0390961680825260038c01875260408083206001600160e01b03198d16845288528083208390558c88528083209183529652948520949094555050506001919091019050613029565b506001600160e01b031983165f90815260018501602052604081206129f491614e3a565b5f516020615db75f395f51905f52545f9065ffffffffffff1680820361323c5762278d0061323e565b805b91505090565b5f516020615db75f395f51905f52545f906601000000000000900461ffff1680820361323c57600a61323e565b5f516020615db75f395f51905f52545f90600160501b90046001600160a01b03168061323c5761329f610d13565b60010154600160481b90046001600160a01b031661323e565b60408051808201909152606081525f60208201525f516020615dd75f395f51905f5280546040805160208084028201810190925282815292919083018282801561332957602002820191905f5260205f20905b81546001600160a01b0316815260019091019060200180831161330b575b5050509183525061333a9050610d13565b600201546001600160a01b0316602082015290565b6001600160a01b0381165f908152600183016020526040812054801580159061277957505f1914159392505050565b5f5f841161339f57604051635d5c34d760e01b815260040160405180910390fd5b5f5f516020615df75f395f51905f52335f90815260209182526040908190208151808301909252546001600160d01b038116808352600160d01b90910465ffffffffffff1692820192909252915085111561340d5760405163f060f88d60e01b815260040160405180910390fd5b806020015165ffffffffffff1642101561343a576040516372aa22b560e11b815260040160405180910390fd5b5f613443613c85565b90505f61345c87845f01516001600160d01b031661464a565b9050805f516020615d975f395f51905f526001015f82825461347e9190615c70565b909155505f905061348d61484f565b90506134aa5f516020615d975f395f51905f526001015482614876565b6134b26113fa565b6134bd90600a615bae565b83516134c99084615870565b6134d39190615c97565b94505f6134de610d13565b600201546001600160a01b03908116915087165f190161350057809650613642565b806001600160a01b0316876001600160a01b031614613642575f6001600160a01b03821615613594576040516370a0823160e01b81523060048201526001600160a01b038316906370a0823190602401602060405180830381865afa15801561356b573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061358f9190615ab4565b613596565b475b90508087116135b857604051635981707960e11b815260040160405180910390fd5b3063da0532fb836135c88a6143dd565b6040516001600160e01b031960e085901b1681526001600160a01b0392831660048201526024810191909152908b166044820152606401602060405180830381865afa15801561361a573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061363e9190615ab4565b9650505b5f61271061364e613244565b61365c9061ffff1689615870565b6136669190615c97565b90506136728188615c70565b9650888710156136955760405163d68d95f760e01b815260040160405180910390fd5b6001600160a01b0388166136dc576136ad338861419d565b6136d7817f0000000000000000000000000000000000000000000000000000000000000000612dc8565b613724565b6136f06001600160a01b03891633896142ff565b6137246001600160a01b0389167f0000000000000000000000000000000000000000000000000000000000000000836142ff565b5050505050509392505050565b5f60ff8210613752576040516299b22b60e31b815260040160405180910390fd5b506001901b16151590565b60ff811061377d576040516299b22b60e31b815260040160405180910390fd5b8154600190911b19169055565b5f5f516020615e375f395f51905f525b54919050565b60607fa33198d1011bad6f8d9b4a537f82cf21cfac49b1430cf1a99c11aaf4d7325fc6805460408051602080840282018101909252828152929190830182828015610f4b57602002820191905f5260205f20905b81546001600160a01b031681526001909101906020018083116137f4575050505050905090565b6001600160a01b0381165f908152600283016020908152604091829020805483518184028101840190945280845260609392830182828015612ffd57602002820191905f5260205f20905f905b82829054906101000a900460e01b6001600160e01b0319168152602001906004019060208260030104928301926001038202915080841161386857509498975050505050505050565b6001600160e01b031982165f908152602084815260408083206001600160a01b03851684529091528120548082036138ec575f915050611140565b6001600160e01b031984165f908152600180870160205260408220805490929161391591615c70565b90505f613923600185615c70565b90508181146139bb575f83838154811061393f5761393f6155f9565b905f5260205f20015f9054906101000a90046001600160a01b031690508084838154811061396f5761396f6155f9565b5f91825260208083209190910180546001600160a01b0319166001600160a01b039485161790556001600160e01b03198b1682528b815260408083209490931682529290925290208490555b828054806139cb576139cb615c83565b5f828152602080822083015f1990810180546001600160a01b03191690559092019092556001600160e01b031989168083528a825260408084206001600160a01b038b1680865290845281852085905560038d0184528185209285529183528084205491845260028c01909252908220805491929091613a4d90600190615c70565b90505f613a5b600185615c70565b9050818114613b12575f838381548110613a7757613a776155f9565b905f5260205f2090600891828204019190066004029054906101000a900460e01b905080848381548110613aad57613aad6155f9565b5f91825260208083206008830401805463ffffffff60079094166004026101000a938402191660e09590951c929092029390931790556001600160a01b038d16815260038f01825260408082206001600160e01b031994909416825292909152208490555b82805480613b2257613b22615c83565b5f828152602080822060085f1990940193840401805463ffffffff600460078716026101000a0219169055919092556001600160a01b038c16825260038e01815260408083206001600160e01b03198f1684529091528120555060019750505050505050509392505050565b6001600160e01b031982165f908152602084815260408083206001600160a01b038516845290915281205415613bc557505f611140565b506001600160e01b031982165f818152600185810160209081526040808420805480850182558186528386200180546001600160a01b0319166001600160a01b0398909816978817905585855254888352818520878652835281852055600288018252808420805480850182558186528386206008820401805463ffffffff60079093166004026101000a928302191660e09a909a1c91909102989098179097559483529454600390960185528382209282529190935291209190915590565b613cc46040518060c001604052805f81526020015f81526020015f6001600160a01b031681526020015f60ff1681526020015f81526020015f81525090565b5f516020615d975f395f51905f525481527ff46fb7ff9ff9a406787c810524417c818e45ab2f1997f38c2555c845d23bb9f7546020820152613d04610d13565b600201546001600160a01b03166040820152613d1e610d13565b60010154600160401b900460ff16606082015260408082015190516305bcc1d160e11b81526001600160a01b0390911660048201523090630b7983a290602401602060405180830381865afa158015613d79573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613d9d9190615c55565b613dba5760405163271fc50760e21b815260040160405180910390fd5b5f613dc882604001516148c8565b90505f8112613ddd5760808201819052613dec565b613de681615caa565b60a08301525b81515f03613e0b576060820151613e0490600a615bae565b8252613e93565b5f613e1461484f565b9050613e24836020015182614876565b5f818460200151613e359190615cc4565b9050805f03613e445750505090565b60208401819052608084015115613e8a5760208401516060850151613e6a90600a615bae565b8560800151613e799190615870565b613e839190615c97565b8452613e90565b50505090565b50505b81515f03613ea057600182525b5f516020615d975f395f51905f5254825114613f015781515f516020615d975f395f51905f52558151604051908152309033907f2cddeba55c7c26241fb3a9428659ae9ecd59df0f2895a705f074f17473009c539060200160405180910390a35b5090565b6001600160a01b0381165f9081526002830160205260408120805490915b818110156140d3575f838281548110613f3e57613f3e6155f9565b5f918252602080832060088304015460079092166004026101000a90910460e01b6001600160e01b0319811680845289835260408085206001600160a01b038b1686528452808520549185526001808c0190945284208054929550909390929091613fa99190615c70565b90505f613fb7600185615c70565b905081811461404f575f838381548110613fd357613fd36155f9565b905f5260205f20015f9054906101000a90046001600160a01b0316905080848381548110614003576140036155f9565b5f91825260208083209190910180546001600160a01b0319166001600160a01b039485161790556001600160e01b0319891682528d815260408083209490931682529290925290208490555b8280548061405f5761405f615c83565b5f828152602080822083015f1990810180546001600160a01b03191690559092019092556001600160e01b03199096168082528b875260408083206001600160a01b038d168452885280832083905560038d0188528083209183529652948520949094555050506001919091019050613f23565b506001600160a01b0383165f90815260028501602052604081206129f491614e58565b5f80614107603060f885901c615c70565b9050600a8110610d0d5760405162461bcd60e51b815260206004820152600b60248201526a139bdd081848191a59da5d60aa1b604482015260640161182f565b80825d5050565b6103e86141596113fa565b61416490600a615bae565b61416e9190615c97565b8110156103e8906141995760405163046a200d60e41b815261ffff909116600482015260240161182f565b5050565b5f826001600160a01b03166108fc836040515f60405180830381858888f193505050503d805f81146141ea576040519150601f19603f3d011682016040523d82523d5f602084013e6141ef565b606091505b5050905080610fda57604051633d2cec6f60e21b815260040160405180910390fd5b6040516001600160a01b0384811660248301528381166044830152606482018390525f91829187169060840160408051601f198184030181529181526020820180516001600160e01b03166323b872dd60e01b179052516142729190614e8a565b5f604051808303815f865af19150503d805f81146142ab576040519150601f19603f3d011682016040523d82523d5f602084013e6142b0565b606091505b50915091508180156142da5750805115806142da5750808060200190518101906142da9190615c55565b6142f75760405163be24f3c560e01b815260040160405180910390fd5b505050505050565b6040516001600160a01b038381166024830152604482018390525f91829186169060640160408051601f198184030181529181526020820180516001600160e01b031663a9059cbb60e01b179052516143589190614e8a565b5f604051808303815f865af19150503d805f8114614391576040519150601f19603f3d011682016040523d82523d5f602084013e614396565b606091505b50915091508180156143c05750805115806143c05750808060200190518101906143c09190615c55565b6114fd5760405163022e258160e11b815260040160405180910390fd5b5f6001600160ff1b03821115613f015760405163123baf0360e11b81526004810183905260240161182f565b5f5f614413613213565b420190505f5f516020615db75f395f51905f5254600160401b900461ffff1690508015614568575f614443613271565b9050856001600160a01b0316816001600160a01b031614614566575f61271061447061ffff851688615870565b61447a9190615c97565b90506144868187615c70565b6001600160a01b0383165f9081525f516020615df75f395f51905f5260205260408120805492985083929091906144c79084906001600160d01b0316615ceb565b92506101000a8154816001600160d01b0302191690836001600160d01b03160217905550836145005f516020615df75f395f51905f5290565b6001600160a01b0384165f8181526020929092526040808320805465ffffffffffff95909516600160d01b026001600160d01b039095169490941790935591515f516020615e175f395f51905f529061455c9085815260200190565b60405180910390a3505b505b6001600160a01b0385165f9081525f516020615df75f395f51905f526020526040812080548692906145a49084906001600160d01b0316615ceb565b92506101000a8154816001600160d01b0302191690836001600160d01b03160217905550816145dd5f516020615df75f395f51905f5290565b6001600160a01b0387165f8181526020929092526040808320805465ffffffffffff95909516600160d01b026001600160d01b039095169490941790935591515f516020615e175f395f51905f52906146399088815260200190565b60405180910390a350919392505050565b5f818310156146af57335f9081525f516020615df75f395f51905f526020526040812080548592906146869084906001600160d01b0316615d0a565b92506101000a8154816001600160d01b0302191690836001600160d01b031602179055506146cb565b335f9081525f516020615df75f395f51905f5260205260408120555b5f516020615db75f395f51905f5254600160401b900461ffff1615614824575f6146f3613271565b9050336001600160a01b03821614614822575f6127105f516020615db75f395f51905f525461472d90600160401b900461ffff1687615870565b6147379190615c97565b90506147438186615c70565b6001600160a01b0383165f9081525f516020615df75f395f51905f5260205260408120805492975083929091906147849084906001600160d01b0316615ceb565b92506101000a8154816001600160d01b0302191690836001600160d01b031602179055504260016147b59190615c0d565b5f516020615df75f395f51905f526001600160a01b0384165f8181526020928352604090819020805465ffffffffffff95909516600160d01b026001600160d01b0390951694909417909355915183815233915f516020615e175f395f51905f52910160405180910390a3505b505b6040518381525f9033905f516020615e175f395f51905f529060200160405180910390a35090919050565b5f7fc1634c3ed93b1f7aa4d725c710ac3b239c1d30894404e630b60009ee3411450f61379a565b5f8112156141995761488a60016008615c70565b6148949083615870565b600861489f83615caa565b6148a99190615870565b111561419957604051630f6e887f60e01b815260040160405180910390fd5b5f516020615e375f395f51905f52545f908180306364397f686148e961378a565b6040518263ffffffff1660e01b815260040161490791815260200190565b5f604051808303815f875af192505050801561494457506040513d5f823e601f3d908101601f191682016040526149419190810190615887565b60015b614950576117f6615a22565b5f5b8151811015614ad2575f82828151811061496e5761496e6155f9565b6020908102919091010151515190505f5b81811015614ac85761499d8785858151811061188a5761188a6155f9565b6149dc576149dc8484815181106149b6576149b66155f9565b6020026020010151602001516149d65f516020615e375f395f51905f5290565b90614c68565b8383815181106149ee576149ee6155f9565b60200260200101515f01518181518110614a0a57614a0a6155f9565b6020026020010151602001519450845f14614ac057838381518110614a3157614a316155f9565b60200260200101515f01518181518110614a4d57614a4d6155f9565b60200260200101515f015195505f614a6d876001600160a01b0316614c94565b9050805f03614a8f57614a8f30888c5f516020615dd75f395f51905f52612c62565b614a998682615cc4565b9050614abe815f03614aac576001614aae565b815b6001600160a01b03891690614cc9565b505b60010161497f565b5050600101614952565b505034614adf8682614d1f565b94505f5f516020615dd75f395f51905f52805460408051602080840282018101909252828152929190830182828015614b3f57602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311614b21575b505050505090505f815190505f816001600160401b03811115614b6457614b64614f14565b604051908082528060200260200182016040528015614b8d578160200160208202803683370190505b5090505f5b82811015614be257614bbd848281518110614baf57614baf6155f9565b602002602001015186614d1f565b828281518110614bcf57614bcf6155f9565b6020908102919091010152600101614b92565b508115614c5c576040516340689ffd60e11b815230906380d13ffa90614c1090869085908e90600401615d29565b602060405180830381865afa158015614c2b573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190614c4f9190615ab4565b614c599089615cc4565b97505b50505050505050919050565b60ff8110614c88576040516299b22b60e31b815260040160405180910390fd5b8154600190911b179055565b5f610d0d614cc360017fc258076af3ab9f64fdba2cc1952be624b688559f0d3c8b5215d9aff5276fc852615c70565b83614dfc565b614199614cf760017fc258076af3ab9f64fdba2cc1952be624b688559f0d3c8b5215d9aff5276fc852615c70565b8383610fda81612bc1614e1986866001600160a01b03165f9081526020919091526040902090565b5f614d32836001600160a01b0316614c94565b90508015614d4d57614d4d6001600160a01b0384165f614cc9565b6001600160a01b038316614d7e57614d6d614d688347615c70565b6143dd565b614d779082615cc4565b9050610d0d565b6040516370a0823160e01b81523060048201526001600160a01b038416906370a0823190602401602060405180830381865afa925050508015614dde575060408051601f3d908101601f19168201909252614ddb91810190615ab4565b60015b614de957505f610d0d565b614df2816143dd565b6127799083615cc4565b6001600160a01b0381165f9081526020839052604081205c611140565b90565b60405180606001604052806003906020820280368337509192915050565b5080545f8255905f5260205f2090810190614e559190614e76565b50565b5080545f825560070160089004905f5260205f2090810190614e5591905b5b80821115613f01575f8155600101614e77565b5f82518060208501845e5f920191825250919050565b6001600160a01b0381168114614e55575f5ffd5b8015158114614e55575f5ffd5b5f5f60408385031215614ed2575f5ffd5b8251614edd81614ea0565b6020840151909250614eee81614eb4565b809150509250929050565b5f60208284031215614f09575f5ffd5b815161114081614ea0565b634e487b7160e01b5f52604160045260245ffd5b608081018181106001600160401b0382111715614f4757614f47614f14565b60405250565b604081018181106001600160401b0382111715614f4757614f47614f14565b601f8201601f191681016001600160401b0381118282101715614f9157614f91614f14565b6040525050565b5f82601f830112614fa7575f5ffd5b81516001600160401b03811115614fc057614fc0614f14565b604051614fd7601f8301601f191660200182614f6c565b818152846020838601011115614feb575f5ffd5b8160208501602083015e5f918101602001919091529392505050565b5f60208284031215615017575f5ffd5b81516001600160401b0381111561502c575f5ffd5b61277984828501614f98565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b5f815160a0845261507a60a0850182615038565b9050602083015184820360208601526150938282615038565b60408581015160ff16908701526060808601516001600160a01b0390811691880191909152608095860151169490950193909352509192915050565b602081525f6111406020830184615066565b602081525f6111406020830184615038565b5f5f60408385031215615104575f5ffd5b823561510f81614ea0565b946020939093013593505050565b5f5f6040838503121561512e575f5ffd5b823561513981614ea0565b91506020830135614eee81614eb4565b5f60208284031215615159575f5ffd5b813561114081614ea0565b5f5f5f60608486031215615176575f5ffd5b833561518181614ea0565b95602085013595506040909401359392505050565b5f5f5f606084860312156151a8575f5ffd5b83356151b381614ea0565b925060208401356151c381614ea0565b929592945050506040919091013590565b5f602082840312156151e4575f5ffd5b81356001600160e01b031981168114611140575f5ffd5b65ffffffffffff815116825261ffff602082015116602083015261ffff604082015116604083015260018060a01b03606082015116606083015260018060a01b0360808201511660808301525050565b60a08101610d0d82846151fb565b5f5f6040838503121561526a575f5ffd5b50508035926020909101359150565b602080825282516040838301528051606084018190525f929190910190829060808501905b808310156152c95783516001600160a01b03168252602093840193600193909301929091019061529e565b50602095909501516001600160a01b031660409490940193909352509192915050565b5f5f5f606084860312156152fe575f5ffd5b8335925060208401359150604084013561531781614ea0565b809150509250925092565b815181526020808301519082015260408101610d0d565b61010081525f61534d610100830186615066565b905061535c60208301856151fb565b825160c0830152602083015160e0830152612779565b5f60208284031215615382575f5ffd5b813565ffffffffffff81168114611140575f5ffd5b5f602082840312156153a7575f5ffd5b813561ffff81168114611140575f5ffd5b5f5f604083850312156153c9575f5ffd5b82356153d481614ea0565b91506020830135614eee81614ea0565b5f8151808452602084019350602083015f5b8281101561541d5781516001600160a01b03168652602095860195909101906001016153f6565b5093949350505050565b602081525f61114060208301846153e4565b5f6001600160401b0382111561545157615451614f14565b5060051b60200190565b5f6020828403121561546b575f5ffd5b81356001600160401b03811115615480575f5ffd5b8201601f81018413615490575f5ffd5b803561549b81615439565b6040516154a88282614f6c565b80915082815260208101915060208360051b8501019250868311156154cb575f5ffd5b6020840193505b828410156154ed5783358252602093840193909101906154d2565b9695505050505050565b602080825282518282018190525f918401906040840190835b818110156155385783516001600160e01b031916835260209384019390920191600101615510565b509095945050505050565b5f5f60208385031215615554575f5ffd5b82356001600160401b03811115615569575f5ffd5b8301601f81018513615579575f5ffd5b80356001600160401b0381111561558e575f5ffd5b8560206060830284010111156155a2575f5ffd5b6020919091019590945092505050565b5f5f5f5f608085870312156155c5575f5ffd5b84356155d081614ea0565b9350602085013592506040850135915060608501356155ee81614ea0565b939692955090935050565b634e487b7160e01b5f52603260045260245ffd5b600181811c9082168061562157607f821691505b60208210810361563f57634e487b7160e01b5f52602260045260245ffd5b50919050565b5f60208284031215615655575f5ffd5b81516001600160401b0381111561566a575f5ffd5b82016080818503121561567b575f5ffd5b60405161568781614f28565b81516001600160401b0381111561569c575f5ffd5b6156a886828501614f98565b82525060208201516001600160c01b0319811681146156c5575f5ffd5b602082015260408201516156d881614ea0565b604082015260609190910151906156ee82614ea0565b60608101919091529392505050565b634e487b7160e01b5f52600160045260245ffd5b5f60208284031215615721575f5ffd5b815160ff81168114611140575f5ffd5b601f821115610fda57805f5260205f20601f840160051c810160208510156157565750805b601f840160051c820191505b818110156114fd575f8155600101615762565b81516001600160401b0381111561578e5761578e614f14565b6157a28161579c845461560d565b84615731565b6020601f8211600181146157d4575f83156157bd5750848201515b5f19600385901b1c1916600184901b1784556114fd565b5f84815260208120601f198516915b8281101561580357878501518255602094850194600190920191016157e3565b508482101561582057868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b604081525f6158416040830185615038565b90506001600160401b0360c01b831660208301529392505050565b634e487b7160e01b5f52601160045260245ffd5b8082028115828204841417610d0d57610d0d61585c565b5f60208284031215615897575f5ffd5b81516001600160401b038111156158ac575f5ffd5b8201601f810184136158bc575f5ffd5b80516158c781615439565b6040516158d48282614f6c565b80915082815260208101915060208360051b8501019250868311156158f7575f5ffd5b602084015b83811015615a175780516001600160401b03811115615919575f5ffd5b85016040818a03601f1901121561592e575f5ffd5b60405161593a81614f4d565b60208201516001600160401b03811115615952575f5ffd5b82016020810190603f018b13615966575f5ffd5b805161597181615439565b60405161597e8282614f6c565b80915082815260208101915060208360061b85010192508d8311156159a1575f5ffd5b6020840193505b828410156159f5576040848f0312156159bf575f5ffd5b6040516159cb81614f4d565b84516159d681614ea0565b81526020858101518183015290835260409094019391909101906159a8565b84525050506040919091015160208083019190915290845292830192016158fc565b509695505050505050565b5f60033d1115614e195760045f5f3e505f5160e01c90565b5f60443d1015615a475790565b6040513d600319016004823e80513d60248201116001600160401b0382111715615a7057505090565b80820180516001600160401b03811115615a8b575050505090565b3d8401600319018282016020011115615aa5575050505090565b6116b760208285010185614f6c565b5f60208284031215615ac4575f5ffd5b5051919050565b6001815b6001841115615b0657808504811115615aea57615aea61585c565b6001841615615af857908102905b60019390931c928002615acf565b935093915050565b5f82615b1c57506001610d0d565b81615b2857505f610d0d565b8160018114615b3e5760028114615b4857615b64565b6001915050610d0d565b60ff841115615b5957615b5961585c565b50506001821b610d0d565b5060208310610133831016604e8410600b8410161715615b87575081810a610d0d565b615b935f198484615acb565b805f1904821115615ba657615ba661585c565b029392505050565b5f61114060ff841683615b0e565b5f60ff821660ff8103615bd157615bd161585c565b60010192915050565b5f60208284031215615bea575f5ffd5b813561114081614eb4565b5f60018201615c0657615c0661585c565b5060010190565b80820180821115610d0d57610d0d61585c565b634e487b7160e01b5f52601260045260245ffd5b5f60ff831680615c4657615c46615c20565b8060ff84160491505092915050565b5f60208284031215615c65575f5ffd5b815161114081614eb4565b81810381811115610d0d57610d0d61585c565b634e487b7160e01b5f52603160045260245ffd5b5f82615ca557615ca5615c20565b500490565b5f600160ff1b8201615cbe57615cbe61585c565b505f0390565b8082018281125f831280158216821582161715615ce357615ce361585c565b505092915050565b6001600160d01b038181168382160190811115610d0d57610d0d61585c565b6001600160d01b038281168282160390811115610d0d57610d0d61585c565b606081525f615d3b60608301866153e4565b82810360208401528085518083526020830191506020870192505f5b81811015615d75578351835260209384019390920191600101615d57565b50506001600160a01b0394909416604093909301929092525090939250505056fef46fb7ff9ff9a406787c810524417c818e45ab2f1997f38c2555c845d23bb9f6e3ed9e7d534645c345f2d15f0c405f8de0227b60eb37bbeb25b26db462415dec3dcde6752c7421366e48f002bbf8d6493462e0e43af349bebb99f0470a12300dfd7547127f88410746fb7969b9adb4f9e9d8d2436aa2d2277b1103542deb7b8eddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efdc487a67cca3fd0341a90d1b8834103014d2a61e6a212e57883f8680b8f9c8311de728329845ca9693f4e251833e4fd20a461e4f39179bee6e55171aedb6dc19a2646970667358221220e77fb648e50d873c65738ce9830fd7ba2623959faac695e1149daed8a58c6e5664736f6c634300081c0033e48b9bb119adfc3bccddcc581484cc6725fe8d292ebfcec7d67b1f93138d8bd9e3ed9e7d534645c345f2d15f0c405f8de0227b60eb37bbeb25b26db462415ded000000000000000000000000e35129a1e0bdb913cf6fd8332e9d3533b5f4147200000000000000000000000002d05a307725d91755c486beafe6697562c9a67a000000000000000000000000a0f9c380ad1e1be09046319fd907335b2b452b37

Deployed Bytecode

0x608060405260043610610275575f3560e01c8063935ef3ad1161014e578063dd62ed3e116100c0578063eb6d3a1111610079578063eb6d3a1114610ab0578063ed4fd3cc14610ae3578063f1fc2f7514610b02578063f7b94b3314610b21578063fb47e01614610b34578063ffa1ad7414610bdf57610284565b8063dd62ed3e146109d7578063df0795aa146109f1578063e496fb4a14610a10578063e598a47514610a3c578063e5c2197e14610a5b578063e7d8724e14610a7a57610284565b8063b390c0ab11610112578063b390c0ab14610907578063b45940f214610926578063b6363cf214610945578063bf7e214f14610964578063d0624bed14610997578063d36ed298146109b857610284565b8063935ef3ad1461089d57806395d89b41146108b157806395f71b16146108c55780639d604df6146108e8578063a9059cbb146105c457610284565b806342377107116101e757806370a08231116101ab57806370a08231146107cd57806378b3dea4146108165780637ace8b8d1461083557806389c06568146108495780638da5cb5b1461086a5780639245290d1461087e57610284565b80634237710714610702578063490c98f514610723578063558a72971461076e5780635624b25b1461078d5780635f5817e3146107ac57610284565b8063156e29f611610239578063156e29f61461063357806318160ddd1461065457806323b872dd14610687578063250e6de0146106a9578063313ce567146106bd5780633658e24b146106e357610284565b8063026b1d5f1461057957806306fdde03146105a3578063095ea7b3146105c45780630dd56740146105f557806313af40351461061457610284565b3661028457610282610c0f565b005b34801561028f575f5ffd5b50610298610c0f565b604080515f80356001600160e01b03191660248084019190915283518084039091018152604490920183526020820180516001600160e01b031663739acf9160e11b17905291517f00000000000000000000000002d05a307725d91755c486beafe6697562c9a67a6001600160a01b03169161031391614e8a565b5f60405180830381855af49150503d805f811461034b576040519150601f19603f3d011682016040523d82523d5f602084013e610350565b606091505b509150505f5f8280602001905181019061036a9190614ec1565b90925090506001600160a01b0382166105375760405163c348fa1960e01b81525f356001600160e01b03191660048201527f000000000000000000000000e35129a1e0bdb913cf6fd8332e9d3533b5f414726001600160a01b03169063c348fa1990602401602060405180830381865afa1580156103ea573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061040e9190614ef9565b91506001600160a01b03821661043757604051630fb1627160e11b815260040160405180910390fd5b816001600160a01b0316632ea6c3f06040518163ffffffff1660e01b81526004015f60405180830381865afa92505050801561049457506040513d5f823e601f3d908101601f191682016040526104919190810190615007565b60015b156104de576040805180820190915260058152640342e322e360dc1b60208201526104bf9082610c5a565b6104dc576040516336e360fb60e01b815260040160405180910390fd5b505b5f516020615e575f395f51905f525f80356001600160e01b0319168152602091825260408082203383529092522054151580610534575061051d610d13565b60010154600160481b90046001600160a01b031633145b90505b365f5f375f6001820361055e575f5f365f865af490503d5f5f3e8061055a573d5ffd5b3d5ff35b5f5f365f865afa90503d5f5f3e80610574573d5ffd5b503d5ff35b348015610584575f5ffd5b5061058d610d37565b60405161059a91906150cf565b60405180910390f35b3480156105ae575f5ffd5b506105b7610ebf565b60405161059a91906150e1565b3480156105cf575f5ffd5b506105e56105de3660046150f3565b5f92915050565b604051901515815260200161059a565b348015610600575f5ffd5b5061028261060f36600461511d565b610f55565b34801561061f575f5ffd5b5061028261062e366004615149565b610fe9565b610646610641366004615164565b611120565b60405190815260200161059a565b34801561065f575f5ffd5b507ff46fb7ff9ff9a406787c810524417c818e45ab2f1997f38c2555c845d23bb9f754610646565b348015610692575f5ffd5b506105e56106a1366004615196565b5f9392505050565b3480156106b4575f5ffd5b50610282611147565b3480156106c8575f5ffd5b506106d16113fa565b60405160ff909116815260200161059a565b3480156106ee575f5ffd5b506102826106fd3660046151d4565b611416565b34801561070d575f5ffd5b50610716611504565b60405161059a919061524b565b34801561072e575f5ffd5b506107567f000000000000000000000000a0f9c380ad1e1be09046319fd907335b2b452b3781565b6040516001600160a01b03909116815260200161059a565b348015610779575f5ffd5b506105e561078836600461511d565b6115b6565b348015610798575f5ffd5b506105b76107a7366004615259565b611646565b3480156107b7575f5ffd5b506107c06116bf565b60405161059a9190615279565b3480156107d8575f5ffd5b506106466107e7366004615149565b6001600160a01b03165f9081525f516020615df75f395f51905f5260205260409020546001600160d01b031690565b348015610821575f5ffd5b506106466108303660046152ec565b6116e0565b348015610840575f5ffd5b5061028261172a565b348015610854575f5ffd5b5061085d611b1b565b60405161059a9190615322565b348015610875575f5ffd5b50610756611bac565b348015610889575f5ffd5b50610282610898366004615149565b611bce565b3480156108a8575f5ffd5b50610646611cff565b3480156108bc575f5ffd5b506105b7611d08565b3480156108d0575f5ffd5b506108d9611e45565b60405161059a93929190615339565b3480156108f3575f5ffd5b50610282610902366004615372565b611ec7565b348015610912575f5ffd5b50610646610921366004615259565b611ffc565b348015610931575f5ffd5b50610282610940366004615397565b61201b565b348015610950575f5ffd5b506105e561095f3660046153b8565b612125565b34801561096f575f5ffd5b506107567f000000000000000000000000e35129a1e0bdb913cf6fd8332e9d3533b5f4147281565b3480156109a2575f5ffd5b506109ab612171565b60405161059a9190615427565b3480156109c3575f5ffd5b506105b76109d236600461545b565b61217b565b3480156109e2575f5ffd5b506106466105de3660046153b8565b3480156109fc575f5ffd5b50610282610a0b366004615149565b61220c565b348015610a1b575f5ffd5b50610a2f610a2a366004615149565b612336565b60405161059a91906154f7565b348015610a47575f5ffd5b506109ab610a563660046151d4565b61234f565b348015610a66575f5ffd5b50610282610a75366004615543565b612368565b348015610a85575f5ffd5b50610a8e6124cc565b604080518251815260208084015190820152918101519082015260600161059a565b348015610abb575f5ffd5b506107567f000000000000000000000000420000000000000000000000000000000000000681565b348015610aee575f5ffd5b50610282610afd366004615397565b61251e565b348015610b0d575f5ffd5b50610282610b1c366004615149565b61262c565b610646610b2f3660046155b2565b612713565b348015610b3f575f5ffd5b50610bb3610b4e366004615149565b6040805180820182525f80825260209182018190526001600160a01b039390931683525f516020615df75f395f51905f528152918190208151808301909252546001600160d01b0381168252600160d01b900465ffffffffffff169181019190915290565b6040805182516001600160d01b0316815260209283015165ffffffffffff16928101929092520161059a565b348015610bea575f5ffd5b506105b7604051806040016040528060058152602001640342e322e360dc1b81525081565b6001600160a01b037f0000000000000000000000003d665d334e99cc6fee0ffc505fc7fc1c1dd3515f163003610c585760405163b08d4e7760e01b815260040160405180910390fd5b565b5f5f610c6584612781565b90505f610c7184612781565b90505f5b6003811015610d0557818160038110610c9057610c906155f9565b6020020151838260038110610ca757610ca76155f9565b60200201511015610cbd575f9350505050610d0d565b818160038110610ccf57610ccf6155f9565b6020020151838260038110610ce657610ce66155f9565b60200201511115610cfd5760019350505050610d0d565b600101610c75565b506001925050505b92915050565b7fe48b9bb119adfc3bccddcc581484cc6725fe8d292ebfcec7d67b1f93138d8bd890565b6040805160a0810182526060808252602082018190525f92820183905281018290526080810182905290610d69610d13565b6040518060c00160405290815f82018054610d839061560d565b80601f0160208091040260200160405190810160405280929190818152602001828054610daf9061560d565b8015610dfa5780601f10610dd157610100808354040283529160200191610dfa565b820191905f5260205f20905b815481529060010190602001808311610ddd57829003601f168201915b505050918352505060018201546001600160c01b031960c082901b1660208084019190915260ff600160401b830481166040808601919091526001600160a01b03600160481b850481166060870152600160e81b9094049091161515608085015260029094015490911660a0928301528251918201909252825181529192508101610e83611d08565b8152602001826040015160ff16815260200182606001516001600160a01b031681526020018260a001516001600160a01b031681525091505090565b6060610ec9610d13565b8054610ed49061560d565b80601f0160208091040260200160405190810160405280929190818152602001828054610f009061560d565b8015610f4b5780601f10610f2257610100808354040283529160200191610f4b565b820191905f5260205f20905b815481529060010190602001808311610f2e57829003601f168201915b5050505050905090565b610f5d610d13565b60010154600160481b90046001600160a01b03163314610f90576040516359dc3ad960e01b815260040160405180910390fd5b7fa33198d1011bad6f8d9b4a537f82cf21cfac49b1430cf1a99c11aaf4d7325fc68115610fdf57610fda3084610fc4610d13565b60020154849291906001600160a01b031661285d565b505050565b610fda81846129fa565b610ff1610d13565b60010154600160481b90046001600160a01b03163314611024576040516359dc3ad960e01b815260040160405180910390fd5b6001600160a01b03811661104b57604051630638d3cf60e11b815260040160405180910390fd5b611053610d13565b600101546001600160a01b03600160481b90910481169082160361108a57604051632d3ab52360e01b815260040160405180910390fd5b5f611093610d13565b60010154600160481b90046001600160a01b03169050816110b2610d13565b60010180547fffffff0000000000000000000000000000000000000000ffffffffffffffffff16600160481b6001600160a01b0393841602179055604051838216918316907f70aea8d848e8a90fb7661b227dc522eb6395c3dac71b63cb59edd5c9899b2364905f90a35050565b5f611129612b5a565b6111368484846001612bc7565b9050611140612f69565b9392505050565b303b1561116757604051637983c05160e01b815260040160405180910390fd5b5f336001600160a01b031663890357306040518163ffffffff1660e01b81526004015f60405180830381865afa1580156111a3573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526111ca9190810190615645565b6040805160c081018252825181526020808401516001600160c01b03191690820152601281830152908201516001600160a01b039081166060808401919091526001608084015283018051821660a084015251929350909116156112dd575f82606001516001600160a01b03163b11611245576112456156fd565b81606001516001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa9250505080156112a3575060408051601f3d908101601f191682019092526112a091810190615711565b60015b6112c0576040516311440b7160e11b815260040160405180910390fd5b60068160ff1610156112d4576112d46156fd565b60ff1660408201525b806112e6610d13565b815181906112f49082615775565b5060208281015160018301805460408087015160608089015160808a015160c09790971c68ffffffffffffffffff1990951694909417600160401b60ff90931692909202919091177fffff000000000000000000000000000000000000000000ffffffffffffffffff16600160481b6001600160a01b039485160260ff60e81b191617600160e81b951515959095029490941790925560a090950151600290940180546001600160a01b0319169486169490941790935585015185830151865192870151935191851694169233927f3a2fbe8bd0946e97170fefca19055b7212080a9a7415726569908565a102f574926113ee929061582f565b60405180910390a45050565b5f611403610d13565b60010154600160401b900460ff16919050565b61141e610d13565b60010154600160481b90046001600160a01b03163314611451576040516359dc3ad960e01b815260040160405180910390fd5b5f516020615e575f395f51905f525f61146a8284612f93565b9050611476828461300a565b80515f5b818110156114fd57846001600160e01b03191683828151811061149f5761149f6155f9565b60200260200101516001600160a01b0316306001600160a01b03167fca7011dbc0b320b7a80ddd4396a3a753c30a8748cd8e4128ac6be6c8fc8eef9e5f6040516114ed911515815260200190565b60405180910390a460010161147a565b5050505050565b6040805160a0810182525f808252602082018190529181018290526060810182905260808101919091526040518060a00160405280611541613213565b65ffffffffffff168152602001611556613244565b61ffff1681526020015f516020615db75f395f51905f5254600160401b900461ffff168152602001611586613271565b6001600160a01b031681526020015f516020615db75f395f51905f52600101546001600160a01b03169052919050565b335f8181527fac0ed3ab25c1c02fcfdfba47b1953f88a6f24e5a50f1076d09054047884e5350602090815260408083206001600160a01b038716808552908352818420805460ff1916871515908117909155825190815291519394909390927fceb576d9f15e4e200fdb5096d64d5dfd667e16def20c1eefd14256d8e3faa267928290030190a350600192915050565b60605f611654836020615870565b6001600160401b0381111561166b5761166b614f14565b6040519080825280601f01601f191660200182016040528015611695576020820181803683370190505b5090505f5b838110156116b7578481015460208083028401015260010161169a565b509392505050565b60408051808201909152606081525f60208201526116db6132b8565b905090565b5f6116e9612b5a565b611702825f516020615dd75f395f51905f525b9061334f565b61171f57604051637609d68760e11b815260040160405180910390fd5b61113684848461337e565b611732610d13565b60010154600160481b90046001600160a01b03163314611765576040516359dc3ad960e01b815260040160405180910390fd5b5f5f516020615dd75f395f51905f5290505f5f516020615e375f395f51905f528054604051630c872fed60e31b8152600481018290529192509060609030906364397f68906024015f604051808303815f875af19250505080156117ea57506040513d5f823e601f3d908101601f191682016040526117e79190810190615887565b60015b611842576117f6615a22565b806308c379a003611838575061180a615a3a565b80611815575061183a565b8060405162461bcd60e51b815260040161182f91906150e1565b60405180910390fd5b505b3d5f5f3e3d5ffd5b5f5b81518110156118d85781818151811061185f5761185f6155f9565b60200260200101515f0151515f14801561189b575061189b8483838151811061188a5761188a6155f9565b602002602001015160200151613731565b156118d0576118d08282815181106118b5576118b56155f9565b6020026020010151602001518661375d90919063ffffffff16565b600101611844565b508454604080516020808402820181019092528281529293505f9291879183018282801561192d57602002820191905f5260205f20905b81546001600160a01b0316815260019091019060200180831161190f575b505083519394505f9250829150505b82811015611b11575f805b8651811015611a0d575f878281518110611963576119636155f9565b6020908102919091010151515190505f5b818110156119f75787858151811061198e5761198e6155f9565b60200260200101516001600160a01b03168984815181106119b1576119b16155f9565b60200260200101515f015182815181106119cd576119cd6155f9565b60200260200101515f01516001600160a01b0316036119ef57600193506119f7565b600101611974565b508215611a045750611a0d565b50600101611947565b5080611b08575f6001600160a01b0316858381518110611a2f57611a2f6155f9565b60200260200101516001600160a01b031603611a4d57479250611ad4565b848281518110611a5f57611a5f6155f9565b60209081029190910101516040516370a0823160e01b81523060048201526001600160a01b03909116906370a0823190602401602060405180830381865afa158015611aad573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611ad19190615ab4565b92505b60018311611b0857611b08858381518110611af157611af16155f9565b60200260200101518a6129fa90919063ffffffff16565b5060010161193c565b5050505050505050565b604080518082019091525f80825260208201525f5f516020615d975f395f51905f525f015490506040518060400160405280825f03611b7a57611b5c610d13565b60010154611b7590600160401b900460ff16600a615bae565b611b7c565b825b81527ff46fb7ff9ff9a406787c810524417c818e45ab2f1997f38c2555c845d23bb9f75460209091015292915050565b5f611bb5610d13565b60010154600160481b90046001600160a01b0316919050565b611bd6610d13565b60010154600160481b90046001600160a01b03163314611c09576040516359dc3ad960e01b815260040160405180910390fd5b336001600160a01b0382161480611c255750611c258133612125565b611c425760405163ccea9e6f60e01b815260040160405180910390fd5b611c4a613271565b6001600160a01b0316816001600160a01b031603611c7b57604051632d3ab52360e01b815260040160405180910390fd5b805f516020615db75f395f51905f5280547fffff0000000000000000000000000000000000000000ffffffffffffffffffff16600160501b6001600160a01b03938416021790556040519082168152309033907fe0f5eb53a7506b986f120aada1efae98697a4d8af5d520a2b826beec9b73e31c906020015b60405180910390a350565b5f6116db61378a565b60605f611d13610d13565b6001015460c01b90505f5b60088160ff16108015611d525750818160ff1660088110611d4157611d416155f9565b1a60f81b6001600160f81b03191615155b15611d695780611d6181615bbc565b915050611d1e565b5f8160ff166001600160401b03811115611d8557611d85614f14565b6040519080825280601f01601f191660200182016040528015611daf576020820181803683370190505b5090505f91505b60088260ff16108015611dea5750828260ff1660088110611dd957611dd96155f9565b1a60f81b6001600160f81b03191615155b1561114057828260ff1660088110611e0457611e046155f9565b1a60f81b818360ff1681518110611e1d57611e1d6155f9565b60200101906001600160f81b03191690815f1a90535081611e3d81615bbc565b925050611db6565b6040805160a08082018352606080835260208084018290525f84860181905282850181905260808086018290528651948501875281855284830182905284870182905292840181905291830182905284518086019095528185528401529091611eac610d37565b611eb4611504565b611ebc611b1b565b925092509250909192565b611ecf610d13565b60010154600160481b90046001600160a01b03163314611f02576040516359dc3ad960e01b815260040160405180910390fd5b6201518065ffffffffffff821610801590611f28575062278d0065ffffffffffff821611155b6201518062278d009091611f615760405163b050fecb60e01b815265ffffffffffff92831660048201529116602482015260440161182f565b5050611f6b613213565b65ffffffffffff168165ffffffffffff1603611f9a57604051632d3ab52360e01b815260040160405180910390fd5b805f516020615db75f395f51905f52805465ffffffffffff191665ffffffffffff928316179055604051908216815230907f65abe17b1ab4c20dc5e52c5b9476b5898c128ee188e0ecc71e0b18c7498e06b5906020015b60405180910390a250565b5f612005612b5a565b6120118383600161337e565b9050610d0d612f69565b612023610d13565b60010154600160481b90046001600160a01b03163314612056576040516359dc3ad960e01b815260040160405180910390fd5b606461ffff82168110156120845760405163385988a560e01b815261ffff909116600482015260240161182f565b505f516020615db75f395f51905f525461ffff600160401b9091048116908216036120c257604051632d3ab52360e01b815260040160405180910390fd5b805f516020615db75f395f51905f52805469ffff00000000000000001916600160401b61ffff938416021790556040519082168152309033907f898ff9e005b9e94b9d353956416e9cd2a7f6c2e09cfb8357b17978aa18ba8f7490602001611cf4565b6001600160a01b039182165f9081527fac0ed3ab25c1c02fcfdfba47b1953f88a6f24e5a50f1076d09054047884e53506020908152604080832093909416825291909152205460ff1690565b60606116db6137a0565b80516060905f61218c826020615870565b6001600160401b038111156121a3576121a3614f14565b6040519080825280601f01601f1916602001820160405280156121cd576020820181803683370190505b5090505f5b828110156116b7575f8582815181106121ed576121ed6155f9565b60209081029190910181015154838202850190910152506001016121d2565b612214610d13565b60010154600160481b90046001600160a01b03163314612247576040516359dc3ad960e01b815260040160405180910390fd5b6001600160a01b0381161561227e576001600160a01b0381163b61227e57604051630a1691a360e11b815260040160405180910390fd5b7fe3ed9e7d534645c345f2d15f0c405f8de0227b60eb37bbeb25b26db462415ded546001600160a01b03908116908216036122cc57604051632d3ab52360e01b815260040160405180910390fd5b7fe3ed9e7d534645c345f2d15f0c405f8de0227b60eb37bbeb25b26db462415ded80546001600160a01b0319166001600160a01b03831690811790915560405130907f173da1498f2256abc1e3ecd01e05f37e663bbd61a3d38e44448faf6105df01f2905f90a350565b6060610d0d5f516020615e575f395f51905f528361381b565b6060610d0d5f516020615e575f395f51905f5283612f93565b612370610d13565b60010154600160481b90046001600160a01b031633146123a3576040516359dc3ad960e01b815260040160405180910390fd5b5f516020615e575f395f51905f52815f5b818110156114fd57368585838181106123cf576123cf6155f9565b90506060020190505f8160400160208101906123eb9190615bda565b61241c5761241761240260408401602085016151d4565b61240f6020850185615149565b8791906138b1565b612444565b61244461242f60408401602085016151d4565b61243c6020850185615149565b879190613b8e565b905080156124c25761245c60408301602084016151d4565b6001600160e01b0319166124736020840184615149565b6001600160a01b0316307fca7011dbc0b320b7a80ddd4396a3a753c30a8748cd8e4128ac6be6c8fc8eef9e6124ae6060870160408801615bda565b604051901515815260200160405180910390a45b50506001016123b4565b6124ed60405180606001604052805f81526020015f81526020015f81525090565b5f6124f6613c85565b60408051606081018252825181526080830151602082015260a0909201519082015292915050565b612526610d13565b60010154600160481b90046001600160a01b03163314612559576040516359dc3ad960e01b815260040160405180910390fd5b5f8161ffff1611801561257257506101f461ffff821611155b6101f49061259a576040516310a31c9b60e21b815261ffff909116600482015260240161182f565b506125a3613244565b61ffff168161ffff16036125ca57604051632d3ab52360e01b815260040160405180910390fd5b805f516020615db75f395f51905f52805467ffff0000000000001916660100000000000061ffff93841602179055604051908216815230907ff56e6a717bc79dc6398f1af61c9c98f532f4697315891b2b23cac09aca95dd2a90602001611ff1565b612634610d13565b60010154600160481b90046001600160a01b03163314612667576040516359dc3ad960e01b815260040160405180910390fd5b5f516020615e575f395f51905f525f612680828461381b565b905061268c8284613f05565b80515f5b818110156114fd578281815181106126aa576126aa6155f9565b60200260200101516001600160e01b031916856001600160a01b0316306001600160a01b03167fca7011dbc0b320b7a80ddd4396a3a753c30a8748cd8e4128ac6be6c8fc8eef9e5f604051612703911515815260200190565b60405180910390a4600101612690565b5f61271c612b5a565b612746827fa33198d1011bad6f8d9b4a537f82cf21cfac49b1430cf1a99c11aaf4d7325fc66116fc565b61276357604051633912a3f560e11b815260040160405180910390fd5b61276f85858585612bc7565b9050612779612f69565b949350505050565b612789614e1c565b815182905f80805b8381101561283a578481815181106127ab576127ab6155f9565b01602001516001600160f81b031916601760f91b036127f157818684600381106127d7576127d76155f9565b6020020152826127e681615bf5565b9350505f9150612832565b61281a858281518110612806576128066155f9565b01602001516001600160f81b0319166140f6565b61282583600a615870565b61282f9190615c0d565b91505b600101612791565b508085836003811061284e5761284e6155f9565b60200201525092949350505050565b806001600160a01b0316826001600160a01b0316146129f4576001600160a01b0382165f90815260018501602052604090205415806128b557506001600160a01b0382165f9081526001850160205260409020545f19145b156129f4576128c6600260ff615c34565b845460ff91909116116128ec57604051636e6010a560e11b815260040160405180910390fd5b6040516305bcc1d160e11b81526001600160a01b038381166004830152841690630b7983a290602401602060405180830381865afa158015612930573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906129549190615c55565b829061297f57604051631f7ddc2d60e21b81526001600160a01b03909116600482015260240161182f565b508354600180820186555f868152602080822090930180546001600160a01b0319166001600160a01b03871690811790915587548183528389018552604092839020559051918252917f0de9c5667496fd561ff9dfa59efdc1c76f7dbf3844adae3d14a3a14d563321b1910160405180910390a25b50505050565b6001600160a01b0381165f9081526001830160205260409020548015801590612a2457505f198114155b15610fda575f612a35600183615c70565b84549091505f90612a4890600190615c70565b9050808214612ad0575f855f018281548110612a6657612a666155f9565b5f9182526020909120015486546001600160a01b0390911691508190879085908110612a9457612a946155f9565b5f91825260208083209190910180546001600160a01b0319166001600160a01b0394851617905592909116815260018701909152604090208390555b8454859080612ae157612ae1615c83565b5f82815260208082205f19908401810180546001600160a01b03191690559283019093556001600160a01b038716808252600189018452604080832093909355915190815290917f0de9c5667496fd561ff9dfa59efdc1c76f7dbf3844adae3d14a3a14d563321b1910160405180910390a25050505050565b7f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f005c15612b9a57604051633ee5aeb560e01b815260040160405180910390fd5b610c5860017f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f005b90614147565b5f6001600160a01b038516612bef5760405163671565e560e01b815260040160405180910390fd5b336001600160a01b0386161480612c0b5750612c0b8533612125565b612c285760405163ccea9e6f60e01b815260040160405180910390fd5b6001600160a01b038216600114612c6a57612c6a3083612c46610d13565b600201546001600160a01b03165f516020615dd75f395f51905f525b92919061285d565b5f612c73613c85565b7fe3ed9e7d534645c345f2d15f0c405f8de0227b60eb37bbeb25b26db462415ded549091506001600160a01b03168015612d2c57604051636c46138f60e11b81526001600160a01b03888116600483015282169063d88c271e90602401602060405180830381865afa158015612ceb573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612d0f9190615c55565b612d2c5760405163a8daabcd60e01b815260040160405180910390fd5b612d358661414e565b5f612710612d41613244565b612d4f9061ffff1689615870565b612d599190615c97565b90505f196001600160a01b03861601612d7457826040015194505b6001600160a01b038516612ddc57863414612da25760405163676f83a160e01b815260040160405180910390fd5b612dd7817f000000000000000000000000a0f9c380ad1e1be09046319fd907335b2b452b375b6001600160a01b03169061419d565b612e25565b612df16001600160a01b03861633308a614211565b612e256001600160a01b0386167f000000000000000000000000a0f9c380ad1e1be09046319fd907335b2b452b37836142ff565b612e2f8188615c70565b965082604001516001600160a01b0316856001600160a01b031614612edd573063da0532fb86612e5e8a6143dd565b60408088015190516001600160e01b031960e086901b1681526001600160a01b0393841660048201526024810192909252919091166044820152606401602060405180830381865afa158015612eb6573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612eda9190615ab4565b96505b825160608401515f9190612ef290600a615bae565b612efc908a615870565b612f069190615c97565b9050805f516020615d975f395f51905f526001015f828254612f289190615c0d565b909155505f9050612f398a83614409565b905087811015612f5c5760405163ac06fcab60e01b815260040160405180910390fd5b9998505050505050505050565b610c585f7f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00612bc1565b6001600160e01b031981165f908152600183016020908152604091829020805483518184028101840190945280845260609392830182828015612ffd57602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311612fdf575b5050505050905092915050565b6001600160e01b031981165f9081526001830160205260408120805490915b818110156131ef575f838281548110613044576130446155f9565b5f9182526020808320909101546001600160a01b031680835260038901825260408084206001600160e01b03198a16855283528084205482855260028b019093528320805491945091929061309b90600190615c70565b90505f6130a9600185615c70565b9050818114613160575f8383815481106130c5576130c56155f9565b905f5260205f2090600891828204019190066004029054906101000a900460e01b9050808483815481106130fb576130fb6155f9565b5f91825260208083206008830401805463ffffffff60079094166004026101000a938402191660e09590951c929092029390931790556001600160a01b038816815260038d01825260408082206001600160e01b031994909416825292909152208490555b8280548061317057613170615c83565b5f828152602080822060085f1990940193840401805463ffffffff600460078716026101000a0219169055919092556001600160a01b0390961680825260038c01875260408083206001600160e01b03198d16845288528083208390558c88528083209183529652948520949094555050506001919091019050613029565b506001600160e01b031983165f90815260018501602052604081206129f491614e3a565b5f516020615db75f395f51905f52545f9065ffffffffffff1680820361323c5762278d0061323e565b805b91505090565b5f516020615db75f395f51905f52545f906601000000000000900461ffff1680820361323c57600a61323e565b5f516020615db75f395f51905f52545f90600160501b90046001600160a01b03168061323c5761329f610d13565b60010154600160481b90046001600160a01b031661323e565b60408051808201909152606081525f60208201525f516020615dd75f395f51905f5280546040805160208084028201810190925282815292919083018282801561332957602002820191905f5260205f20905b81546001600160a01b0316815260019091019060200180831161330b575b5050509183525061333a9050610d13565b600201546001600160a01b0316602082015290565b6001600160a01b0381165f908152600183016020526040812054801580159061277957505f1914159392505050565b5f5f841161339f57604051635d5c34d760e01b815260040160405180910390fd5b5f5f516020615df75f395f51905f52335f90815260209182526040908190208151808301909252546001600160d01b038116808352600160d01b90910465ffffffffffff1692820192909252915085111561340d5760405163f060f88d60e01b815260040160405180910390fd5b806020015165ffffffffffff1642101561343a576040516372aa22b560e11b815260040160405180910390fd5b5f613443613c85565b90505f61345c87845f01516001600160d01b031661464a565b9050805f516020615d975f395f51905f526001015f82825461347e9190615c70565b909155505f905061348d61484f565b90506134aa5f516020615d975f395f51905f526001015482614876565b6134b26113fa565b6134bd90600a615bae565b83516134c99084615870565b6134d39190615c97565b94505f6134de610d13565b600201546001600160a01b03908116915087165f190161350057809650613642565b806001600160a01b0316876001600160a01b031614613642575f6001600160a01b03821615613594576040516370a0823160e01b81523060048201526001600160a01b038316906370a0823190602401602060405180830381865afa15801561356b573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061358f9190615ab4565b613596565b475b90508087116135b857604051635981707960e11b815260040160405180910390fd5b3063da0532fb836135c88a6143dd565b6040516001600160e01b031960e085901b1681526001600160a01b0392831660048201526024810191909152908b166044820152606401602060405180830381865afa15801561361a573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061363e9190615ab4565b9650505b5f61271061364e613244565b61365c9061ffff1689615870565b6136669190615c97565b90506136728188615c70565b9650888710156136955760405163d68d95f760e01b815260040160405180910390fd5b6001600160a01b0388166136dc576136ad338861419d565b6136d7817f000000000000000000000000a0f9c380ad1e1be09046319fd907335b2b452b37612dc8565b613724565b6136f06001600160a01b03891633896142ff565b6137246001600160a01b0389167f000000000000000000000000a0f9c380ad1e1be09046319fd907335b2b452b37836142ff565b5050505050509392505050565b5f60ff8210613752576040516299b22b60e31b815260040160405180910390fd5b506001901b16151590565b60ff811061377d576040516299b22b60e31b815260040160405180910390fd5b8154600190911b19169055565b5f5f516020615e375f395f51905f525b54919050565b60607fa33198d1011bad6f8d9b4a537f82cf21cfac49b1430cf1a99c11aaf4d7325fc6805460408051602080840282018101909252828152929190830182828015610f4b57602002820191905f5260205f20905b81546001600160a01b031681526001909101906020018083116137f4575050505050905090565b6001600160a01b0381165f908152600283016020908152604091829020805483518184028101840190945280845260609392830182828015612ffd57602002820191905f5260205f20905f905b82829054906101000a900460e01b6001600160e01b0319168152602001906004019060208260030104928301926001038202915080841161386857509498975050505050505050565b6001600160e01b031982165f908152602084815260408083206001600160a01b03851684529091528120548082036138ec575f915050611140565b6001600160e01b031984165f908152600180870160205260408220805490929161391591615c70565b90505f613923600185615c70565b90508181146139bb575f83838154811061393f5761393f6155f9565b905f5260205f20015f9054906101000a90046001600160a01b031690508084838154811061396f5761396f6155f9565b5f91825260208083209190910180546001600160a01b0319166001600160a01b039485161790556001600160e01b03198b1682528b815260408083209490931682529290925290208490555b828054806139cb576139cb615c83565b5f828152602080822083015f1990810180546001600160a01b03191690559092019092556001600160e01b031989168083528a825260408084206001600160a01b038b1680865290845281852085905560038d0184528185209285529183528084205491845260028c01909252908220805491929091613a4d90600190615c70565b90505f613a5b600185615c70565b9050818114613b12575f838381548110613a7757613a776155f9565b905f5260205f2090600891828204019190066004029054906101000a900460e01b905080848381548110613aad57613aad6155f9565b5f91825260208083206008830401805463ffffffff60079094166004026101000a938402191660e09590951c929092029390931790556001600160a01b038d16815260038f01825260408082206001600160e01b031994909416825292909152208490555b82805480613b2257613b22615c83565b5f828152602080822060085f1990940193840401805463ffffffff600460078716026101000a0219169055919092556001600160a01b038c16825260038e01815260408083206001600160e01b03198f1684529091528120555060019750505050505050509392505050565b6001600160e01b031982165f908152602084815260408083206001600160a01b038516845290915281205415613bc557505f611140565b506001600160e01b031982165f818152600185810160209081526040808420805480850182558186528386200180546001600160a01b0319166001600160a01b0398909816978817905585855254888352818520878652835281852055600288018252808420805480850182558186528386206008820401805463ffffffff60079093166004026101000a928302191660e09a909a1c91909102989098179097559483529454600390960185528382209282529190935291209190915590565b613cc46040518060c001604052805f81526020015f81526020015f6001600160a01b031681526020015f60ff1681526020015f81526020015f81525090565b5f516020615d975f395f51905f525481527ff46fb7ff9ff9a406787c810524417c818e45ab2f1997f38c2555c845d23bb9f7546020820152613d04610d13565b600201546001600160a01b03166040820152613d1e610d13565b60010154600160401b900460ff16606082015260408082015190516305bcc1d160e11b81526001600160a01b0390911660048201523090630b7983a290602401602060405180830381865afa158015613d79573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613d9d9190615c55565b613dba5760405163271fc50760e21b815260040160405180910390fd5b5f613dc882604001516148c8565b90505f8112613ddd5760808201819052613dec565b613de681615caa565b60a08301525b81515f03613e0b576060820151613e0490600a615bae565b8252613e93565b5f613e1461484f565b9050613e24836020015182614876565b5f818460200151613e359190615cc4565b9050805f03613e445750505090565b60208401819052608084015115613e8a5760208401516060850151613e6a90600a615bae565b8560800151613e799190615870565b613e839190615c97565b8452613e90565b50505090565b50505b81515f03613ea057600182525b5f516020615d975f395f51905f5254825114613f015781515f516020615d975f395f51905f52558151604051908152309033907f2cddeba55c7c26241fb3a9428659ae9ecd59df0f2895a705f074f17473009c539060200160405180910390a35b5090565b6001600160a01b0381165f9081526002830160205260408120805490915b818110156140d3575f838281548110613f3e57613f3e6155f9565b5f918252602080832060088304015460079092166004026101000a90910460e01b6001600160e01b0319811680845289835260408085206001600160a01b038b1686528452808520549185526001808c0190945284208054929550909390929091613fa99190615c70565b90505f613fb7600185615c70565b905081811461404f575f838381548110613fd357613fd36155f9565b905f5260205f20015f9054906101000a90046001600160a01b0316905080848381548110614003576140036155f9565b5f91825260208083209190910180546001600160a01b0319166001600160a01b039485161790556001600160e01b0319891682528d815260408083209490931682529290925290208490555b8280548061405f5761405f615c83565b5f828152602080822083015f1990810180546001600160a01b03191690559092019092556001600160e01b03199096168082528b875260408083206001600160a01b038d168452885280832083905560038d0188528083209183529652948520949094555050506001919091019050613f23565b506001600160a01b0383165f90815260028501602052604081206129f491614e58565b5f80614107603060f885901c615c70565b9050600a8110610d0d5760405162461bcd60e51b815260206004820152600b60248201526a139bdd081848191a59da5d60aa1b604482015260640161182f565b80825d5050565b6103e86141596113fa565b61416490600a615bae565b61416e9190615c97565b8110156103e8906141995760405163046a200d60e41b815261ffff909116600482015260240161182f565b5050565b5f826001600160a01b03166108fc836040515f60405180830381858888f193505050503d805f81146141ea576040519150601f19603f3d011682016040523d82523d5f602084013e6141ef565b606091505b5050905080610fda57604051633d2cec6f60e21b815260040160405180910390fd5b6040516001600160a01b0384811660248301528381166044830152606482018390525f91829187169060840160408051601f198184030181529181526020820180516001600160e01b03166323b872dd60e01b179052516142729190614e8a565b5f604051808303815f865af19150503d805f81146142ab576040519150601f19603f3d011682016040523d82523d5f602084013e6142b0565b606091505b50915091508180156142da5750805115806142da5750808060200190518101906142da9190615c55565b6142f75760405163be24f3c560e01b815260040160405180910390fd5b505050505050565b6040516001600160a01b038381166024830152604482018390525f91829186169060640160408051601f198184030181529181526020820180516001600160e01b031663a9059cbb60e01b179052516143589190614e8a565b5f604051808303815f865af19150503d805f8114614391576040519150601f19603f3d011682016040523d82523d5f602084013e614396565b606091505b50915091508180156143c05750805115806143c05750808060200190518101906143c09190615c55565b6114fd5760405163022e258160e11b815260040160405180910390fd5b5f6001600160ff1b03821115613f015760405163123baf0360e11b81526004810183905260240161182f565b5f5f614413613213565b420190505f5f516020615db75f395f51905f5254600160401b900461ffff1690508015614568575f614443613271565b9050856001600160a01b0316816001600160a01b031614614566575f61271061447061ffff851688615870565b61447a9190615c97565b90506144868187615c70565b6001600160a01b0383165f9081525f516020615df75f395f51905f5260205260408120805492985083929091906144c79084906001600160d01b0316615ceb565b92506101000a8154816001600160d01b0302191690836001600160d01b03160217905550836145005f516020615df75f395f51905f5290565b6001600160a01b0384165f8181526020929092526040808320805465ffffffffffff95909516600160d01b026001600160d01b039095169490941790935591515f516020615e175f395f51905f529061455c9085815260200190565b60405180910390a3505b505b6001600160a01b0385165f9081525f516020615df75f395f51905f526020526040812080548692906145a49084906001600160d01b0316615ceb565b92506101000a8154816001600160d01b0302191690836001600160d01b03160217905550816145dd5f516020615df75f395f51905f5290565b6001600160a01b0387165f8181526020929092526040808320805465ffffffffffff95909516600160d01b026001600160d01b039095169490941790935591515f516020615e175f395f51905f52906146399088815260200190565b60405180910390a350919392505050565b5f818310156146af57335f9081525f516020615df75f395f51905f526020526040812080548592906146869084906001600160d01b0316615d0a565b92506101000a8154816001600160d01b0302191690836001600160d01b031602179055506146cb565b335f9081525f516020615df75f395f51905f5260205260408120555b5f516020615db75f395f51905f5254600160401b900461ffff1615614824575f6146f3613271565b9050336001600160a01b03821614614822575f6127105f516020615db75f395f51905f525461472d90600160401b900461ffff1687615870565b6147379190615c97565b90506147438186615c70565b6001600160a01b0383165f9081525f516020615df75f395f51905f5260205260408120805492975083929091906147849084906001600160d01b0316615ceb565b92506101000a8154816001600160d01b0302191690836001600160d01b031602179055504260016147b59190615c0d565b5f516020615df75f395f51905f526001600160a01b0384165f8181526020928352604090819020805465ffffffffffff95909516600160d01b026001600160d01b0390951694909417909355915183815233915f516020615e175f395f51905f52910160405180910390a3505b505b6040518381525f9033905f516020615e175f395f51905f529060200160405180910390a35090919050565b5f7fc1634c3ed93b1f7aa4d725c710ac3b239c1d30894404e630b60009ee3411450f61379a565b5f8112156141995761488a60016008615c70565b6148949083615870565b600861489f83615caa565b6148a99190615870565b111561419957604051630f6e887f60e01b815260040160405180910390fd5b5f516020615e375f395f51905f52545f908180306364397f686148e961378a565b6040518263ffffffff1660e01b815260040161490791815260200190565b5f604051808303815f875af192505050801561494457506040513d5f823e601f3d908101601f191682016040526149419190810190615887565b60015b614950576117f6615a22565b5f5b8151811015614ad2575f82828151811061496e5761496e6155f9565b6020908102919091010151515190505f5b81811015614ac85761499d8785858151811061188a5761188a6155f9565b6149dc576149dc8484815181106149b6576149b66155f9565b6020026020010151602001516149d65f516020615e375f395f51905f5290565b90614c68565b8383815181106149ee576149ee6155f9565b60200260200101515f01518181518110614a0a57614a0a6155f9565b6020026020010151602001519450845f14614ac057838381518110614a3157614a316155f9565b60200260200101515f01518181518110614a4d57614a4d6155f9565b60200260200101515f015195505f614a6d876001600160a01b0316614c94565b9050805f03614a8f57614a8f30888c5f516020615dd75f395f51905f52612c62565b614a998682615cc4565b9050614abe815f03614aac576001614aae565b815b6001600160a01b03891690614cc9565b505b60010161497f565b5050600101614952565b505034614adf8682614d1f565b94505f5f516020615dd75f395f51905f52805460408051602080840282018101909252828152929190830182828015614b3f57602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311614b21575b505050505090505f815190505f816001600160401b03811115614b6457614b64614f14565b604051908082528060200260200182016040528015614b8d578160200160208202803683370190505b5090505f5b82811015614be257614bbd848281518110614baf57614baf6155f9565b602002602001015186614d1f565b828281518110614bcf57614bcf6155f9565b6020908102919091010152600101614b92565b508115614c5c576040516340689ffd60e11b815230906380d13ffa90614c1090869085908e90600401615d29565b602060405180830381865afa158015614c2b573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190614c4f9190615ab4565b614c599089615cc4565b97505b50505050505050919050565b60ff8110614c88576040516299b22b60e31b815260040160405180910390fd5b8154600190911b179055565b5f610d0d614cc360017fc258076af3ab9f64fdba2cc1952be624b688559f0d3c8b5215d9aff5276fc852615c70565b83614dfc565b614199614cf760017fc258076af3ab9f64fdba2cc1952be624b688559f0d3c8b5215d9aff5276fc852615c70565b8383610fda81612bc1614e1986866001600160a01b03165f9081526020919091526040902090565b5f614d32836001600160a01b0316614c94565b90508015614d4d57614d4d6001600160a01b0384165f614cc9565b6001600160a01b038316614d7e57614d6d614d688347615c70565b6143dd565b614d779082615cc4565b9050610d0d565b6040516370a0823160e01b81523060048201526001600160a01b038416906370a0823190602401602060405180830381865afa925050508015614dde575060408051601f3d908101601f19168201909252614ddb91810190615ab4565b60015b614de957505f610d0d565b614df2816143dd565b6127799083615cc4565b6001600160a01b0381165f9081526020839052604081205c611140565b90565b60405180606001604052806003906020820280368337509192915050565b5080545f8255905f5260205f2090810190614e559190614e76565b50565b5080545f825560070160089004905f5260205f2090810190614e5591905b5b80821115613f01575f8155600101614e77565b5f82518060208501845e5f920191825250919050565b6001600160a01b0381168114614e55575f5ffd5b8015158114614e55575f5ffd5b5f5f60408385031215614ed2575f5ffd5b8251614edd81614ea0565b6020840151909250614eee81614eb4565b809150509250929050565b5f60208284031215614f09575f5ffd5b815161114081614ea0565b634e487b7160e01b5f52604160045260245ffd5b608081018181106001600160401b0382111715614f4757614f47614f14565b60405250565b604081018181106001600160401b0382111715614f4757614f47614f14565b601f8201601f191681016001600160401b0381118282101715614f9157614f91614f14565b6040525050565b5f82601f830112614fa7575f5ffd5b81516001600160401b03811115614fc057614fc0614f14565b604051614fd7601f8301601f191660200182614f6c565b818152846020838601011115614feb575f5ffd5b8160208501602083015e5f918101602001919091529392505050565b5f60208284031215615017575f5ffd5b81516001600160401b0381111561502c575f5ffd5b61277984828501614f98565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b5f815160a0845261507a60a0850182615038565b9050602083015184820360208601526150938282615038565b60408581015160ff16908701526060808601516001600160a01b0390811691880191909152608095860151169490950193909352509192915050565b602081525f6111406020830184615066565b602081525f6111406020830184615038565b5f5f60408385031215615104575f5ffd5b823561510f81614ea0565b946020939093013593505050565b5f5f6040838503121561512e575f5ffd5b823561513981614ea0565b91506020830135614eee81614eb4565b5f60208284031215615159575f5ffd5b813561114081614ea0565b5f5f5f60608486031215615176575f5ffd5b833561518181614ea0565b95602085013595506040909401359392505050565b5f5f5f606084860312156151a8575f5ffd5b83356151b381614ea0565b925060208401356151c381614ea0565b929592945050506040919091013590565b5f602082840312156151e4575f5ffd5b81356001600160e01b031981168114611140575f5ffd5b65ffffffffffff815116825261ffff602082015116602083015261ffff604082015116604083015260018060a01b03606082015116606083015260018060a01b0360808201511660808301525050565b60a08101610d0d82846151fb565b5f5f6040838503121561526a575f5ffd5b50508035926020909101359150565b602080825282516040838301528051606084018190525f929190910190829060808501905b808310156152c95783516001600160a01b03168252602093840193600193909301929091019061529e565b50602095909501516001600160a01b031660409490940193909352509192915050565b5f5f5f606084860312156152fe575f5ffd5b8335925060208401359150604084013561531781614ea0565b809150509250925092565b815181526020808301519082015260408101610d0d565b61010081525f61534d610100830186615066565b905061535c60208301856151fb565b825160c0830152602083015160e0830152612779565b5f60208284031215615382575f5ffd5b813565ffffffffffff81168114611140575f5ffd5b5f602082840312156153a7575f5ffd5b813561ffff81168114611140575f5ffd5b5f5f604083850312156153c9575f5ffd5b82356153d481614ea0565b91506020830135614eee81614ea0565b5f8151808452602084019350602083015f5b8281101561541d5781516001600160a01b03168652602095860195909101906001016153f6565b5093949350505050565b602081525f61114060208301846153e4565b5f6001600160401b0382111561545157615451614f14565b5060051b60200190565b5f6020828403121561546b575f5ffd5b81356001600160401b03811115615480575f5ffd5b8201601f81018413615490575f5ffd5b803561549b81615439565b6040516154a88282614f6c565b80915082815260208101915060208360051b8501019250868311156154cb575f5ffd5b6020840193505b828410156154ed5783358252602093840193909101906154d2565b9695505050505050565b602080825282518282018190525f918401906040840190835b818110156155385783516001600160e01b031916835260209384019390920191600101615510565b509095945050505050565b5f5f60208385031215615554575f5ffd5b82356001600160401b03811115615569575f5ffd5b8301601f81018513615579575f5ffd5b80356001600160401b0381111561558e575f5ffd5b8560206060830284010111156155a2575f5ffd5b6020919091019590945092505050565b5f5f5f5f608085870312156155c5575f5ffd5b84356155d081614ea0565b9350602085013592506040850135915060608501356155ee81614ea0565b939692955090935050565b634e487b7160e01b5f52603260045260245ffd5b600181811c9082168061562157607f821691505b60208210810361563f57634e487b7160e01b5f52602260045260245ffd5b50919050565b5f60208284031215615655575f5ffd5b81516001600160401b0381111561566a575f5ffd5b82016080818503121561567b575f5ffd5b60405161568781614f28565b81516001600160401b0381111561569c575f5ffd5b6156a886828501614f98565b82525060208201516001600160c01b0319811681146156c5575f5ffd5b602082015260408201516156d881614ea0565b604082015260609190910151906156ee82614ea0565b60608101919091529392505050565b634e487b7160e01b5f52600160045260245ffd5b5f60208284031215615721575f5ffd5b815160ff81168114611140575f5ffd5b601f821115610fda57805f5260205f20601f840160051c810160208510156157565750805b601f840160051c820191505b818110156114fd575f8155600101615762565b81516001600160401b0381111561578e5761578e614f14565b6157a28161579c845461560d565b84615731565b6020601f8211600181146157d4575f83156157bd5750848201515b5f19600385901b1c1916600184901b1784556114fd565b5f84815260208120601f198516915b8281101561580357878501518255602094850194600190920191016157e3565b508482101561582057868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b604081525f6158416040830185615038565b90506001600160401b0360c01b831660208301529392505050565b634e487b7160e01b5f52601160045260245ffd5b8082028115828204841417610d0d57610d0d61585c565b5f60208284031215615897575f5ffd5b81516001600160401b038111156158ac575f5ffd5b8201601f810184136158bc575f5ffd5b80516158c781615439565b6040516158d48282614f6c565b80915082815260208101915060208360051b8501019250868311156158f7575f5ffd5b602084015b83811015615a175780516001600160401b03811115615919575f5ffd5b85016040818a03601f1901121561592e575f5ffd5b60405161593a81614f4d565b60208201516001600160401b03811115615952575f5ffd5b82016020810190603f018b13615966575f5ffd5b805161597181615439565b60405161597e8282614f6c565b80915082815260208101915060208360061b85010192508d8311156159a1575f5ffd5b6020840193505b828410156159f5576040848f0312156159bf575f5ffd5b6040516159cb81614f4d565b84516159d681614ea0565b81526020858101518183015290835260409094019391909101906159a8565b84525050506040919091015160208083019190915290845292830192016158fc565b509695505050505050565b5f60033d1115614e195760045f5f3e505f5160e01c90565b5f60443d1015615a475790565b6040513d600319016004823e80513d60248201116001600160401b0382111715615a7057505090565b80820180516001600160401b03811115615a8b575050505090565b3d8401600319018282016020011115615aa5575050505090565b6116b760208285010185614f6c565b5f60208284031215615ac4575f5ffd5b5051919050565b6001815b6001841115615b0657808504811115615aea57615aea61585c565b6001841615615af857908102905b60019390931c928002615acf565b935093915050565b5f82615b1c57506001610d0d565b81615b2857505f610d0d565b8160018114615b3e5760028114615b4857615b64565b6001915050610d0d565b60ff841115615b5957615b5961585c565b50506001821b610d0d565b5060208310610133831016604e8410600b8410161715615b87575081810a610d0d565b615b935f198484615acb565b805f1904821115615ba657615ba661585c565b029392505050565b5f61114060ff841683615b0e565b5f60ff821660ff8103615bd157615bd161585c565b60010192915050565b5f60208284031215615bea575f5ffd5b813561114081614eb4565b5f60018201615c0657615c0661585c565b5060010190565b80820180821115610d0d57610d0d61585c565b634e487b7160e01b5f52601260045260245ffd5b5f60ff831680615c4657615c46615c20565b8060ff84160491505092915050565b5f60208284031215615c65575f5ffd5b815161114081614eb4565b81810381811115610d0d57610d0d61585c565b634e487b7160e01b5f52603160045260245ffd5b5f82615ca557615ca5615c20565b500490565b5f600160ff1b8201615cbe57615cbe61585c565b505f0390565b8082018281125f831280158216821582161715615ce357615ce361585c565b505092915050565b6001600160d01b038181168382160190811115610d0d57610d0d61585c565b6001600160d01b038281168282160390811115610d0d57610d0d61585c565b606081525f615d3b60608301866153e4565b82810360208401528085518083526020830191506020870192505f5b81811015615d75578351835260209384019390920191600101615d57565b50506001600160a01b0394909416604093909301929092525090939250505056fef46fb7ff9ff9a406787c810524417c818e45ab2f1997f38c2555c845d23bb9f6e3ed9e7d534645c345f2d15f0c405f8de0227b60eb37bbeb25b26db462415dec3dcde6752c7421366e48f002bbf8d6493462e0e43af349bebb99f0470a12300dfd7547127f88410746fb7969b9adb4f9e9d8d2436aa2d2277b1103542deb7b8eddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efdc487a67cca3fd0341a90d1b8834103014d2a61e6a212e57883f8680b8f9c8311de728329845ca9693f4e251833e4fd20a461e4f39179bee6e55171aedb6dc19a2646970667358221220e77fb648e50d873c65738ce9830fd7ba2623959faac695e1149daed8a58c6e5664736f6c634300081c0033

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

000000000000000000000000e35129a1e0bdb913cf6fd8332e9d3533b5f4147200000000000000000000000002d05a307725d91755c486beafe6697562c9a67a000000000000000000000000a0f9c380ad1e1be09046319fd907335b2b452b37

-----Decoded View---------------
Arg [0] : authority (address): 0xe35129A1E0BdB913CF6Fd8332E9d3533b5F41472
Arg [1] : extensionsMap (address): 0x02d05A307725d91755C486BEafE6697562c9A67A
Arg [2] : tokenJar (address): 0xA0F9C380ad1E1be09046319fd907335B2B452B37

-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 000000000000000000000000e35129a1e0bdb913cf6fd8332e9d3533b5f41472
Arg [1] : 00000000000000000000000002d05a307725d91755c486beafe6697562c9a67a
Arg [2] : 000000000000000000000000a0f9c380ad1e1be09046319fd907335b2b452b37


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]

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.