// File: @openzeppelin/contracts/access/Ownable.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)

pragma solidity ^0.8.0;

import "../utils/Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {
        _transferOwnership(_msgSender());
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

// File: @openzeppelin/contracts/proxy/Clones.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (proxy/Clones.sol)

pragma solidity ^0.8.0;

/**
 * @dev https://eips.ethereum.org/EIPS/eip-1167[EIP 1167] is a standard for
 * deploying minimal proxy contracts, also known as "clones".
 *
 * > To simply and cheaply clone contract functionality in an immutable way, this standard specifies
 * > a minimal bytecode implementation that delegates all calls to a known, fixed address.
 *
 * The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2`
 * (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the
 * deterministic method.
 *
 * _Available since v3.4._
 */
library Clones {
    /**
     * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
     *
     * This function uses the create opcode, which should never revert.
     */
    function clone(address implementation) internal returns (address instance) {
        /// @solidity memory-safe-assembly
        assembly {
            // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes
            // of the `implementation` address with the bytecode before the address.
            mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
            // Packs the remaining 17 bytes of `implementation` with the bytecode after the address.
            mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))
            instance := create(0, 0x09, 0x37)
        }
        require(instance != address(0), "ERC1167: create failed");
    }

    /**
     * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
     *
     * This function uses the create2 opcode and a `salt` to deterministically deploy
     * the clone. Using the same `implementation` and `salt` multiple time will revert, since
     * the clones cannot be deployed twice at the same address.
     */
    function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) {
        /// @solidity memory-safe-assembly
        assembly {
            // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes
            // of the `implementation` address with the bytecode before the address.
            mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
            // Packs the remaining 17 bytes of `implementation` with the bytecode after the address.
            mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))
            instance := create2(0, 0x09, 0x37, salt)
        }
        require(instance != address(0), "ERC1167: create2 failed");
    }

    /**
     * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
     */
    function predictDeterministicAddress(
        address implementation,
        bytes32 salt,
        address deployer
    ) internal pure returns (address predicted) {
        /// @solidity memory-safe-assembly
        assembly {
            let ptr := mload(0x40)
            mstore(add(ptr, 0x38), deployer)
            mstore(add(ptr, 0x24), 0x5af43d82803e903d91602b57fd5bf3ff)
            mstore(add(ptr, 0x14), implementation)
            mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73)
            mstore(add(ptr, 0x58), salt)
            mstore(add(ptr, 0x78), keccak256(add(ptr, 0x0c), 0x37))
            predicted := keccak256(add(ptr, 0x43), 0x55)
        }
    }

    /**
     * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
     */
    function predictDeterministicAddress(address implementation, bytes32 salt)
        internal
        view
        returns (address predicted)
    {
        return predictDeterministicAddress(implementation, salt, address(this));
    }
}

// File: @openzeppelin/contracts/utils/Context.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}

// File: @openzeppelin/contracts/utils/math/Math.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    enum Rounding {
        Down, // Toward negative infinity
        Up, // Toward infinity
        Zero // Toward zero
    }

    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow.
        return (a & b) + (a ^ b) / 2;
    }

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds up instead
     * of rounding down.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

    /**
     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
     * with further edits by Uniswap Labs also under MIT license.
     */
    function mulDiv(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2^256 + prod0.
            uint256 prod0; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod0 := mul(x, y)
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            require(denominator > prod1);

            ///////////////////////////////////////////////
            // 512 by 256 division.
            ///////////////////////////////////////////////

            // Make division exact by subtracting the remainder from [prod1 prod0].
            uint256 remainder;
            assembly {
                // Compute remainder using mulmod.
                remainder := mulmod(x, y, denominator)

                // Subtract 256 bit number from 512 bit number.
                prod1 := sub(prod1, gt(remainder, prod0))
                prod0 := sub(prod0, remainder)
            }

            // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
            // See https://cs.stackexchange.com/q/138556/92363.

            // Does not overflow because the denominator cannot be zero at this stage in the function.
            uint256 twos = denominator & (~denominator + 1);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

                // Divide [prod1 prod0] by twos.
                prod0 := div(prod0, twos)

                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                twos := add(div(sub(0, twos), twos), 1)
            }

            // Shift in bits from prod1 into prod0.
            prod0 |= prod1 * twos;

            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
            // four bits. That is, denominator * inv = 1 mod 2^4.
            uint256 inverse = (3 * denominator) ^ 2;

            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
            // in modular arithmetic, doubling the correct bits in each step.
            inverse *= 2 - denominator * inverse; // inverse mod 2^8
            inverse *= 2 - denominator * inverse; // inverse mod 2^16
            inverse *= 2 - denominator * inverse; // inverse mod 2^32
            inverse *= 2 - denominator * inverse; // inverse mod 2^64
            inverse *= 2 - denominator * inverse; // inverse mod 2^128
            inverse *= 2 - denominator * inverse; // inverse mod 2^256

            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
            // is no longer required.
            result = prod0 * inverse;
            return result;
        }
    }

    /**
     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(
        uint256 x,
        uint256 y,
        uint256 denominator,
        Rounding rounding
    ) internal pure returns (uint256) {
        uint256 result = mulDiv(x, y, denominator);
        if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
     *
     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        //
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
        //
        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
        //
        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1 << (log2(a) >> 1);

        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
        // into the expected uint128 result.
        unchecked {
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            return min(result, a / result);
        }
    }

    /**
     * @notice Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = sqrt(a);
            return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 128;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 64;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 32;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 16;
            }
            if (value >> 8 > 0) {
                value >>= 8;
                result += 8;
            }
            if (value >> 4 > 0) {
                value >>= 4;
                result += 4;
            }
            if (value >> 2 > 0) {
                value >>= 2;
                result += 2;
            }
            if (value >> 1 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log2(value);
            return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >= 10**64) {
                value /= 10**64;
                result += 64;
            }
            if (value >= 10**32) {
                value /= 10**32;
                result += 32;
            }
            if (value >= 10**16) {
                value /= 10**16;
                result += 16;
            }
            if (value >= 10**8) {
                value /= 10**8;
                result += 8;
            }
            if (value >= 10**4) {
                value /= 10**4;
                result += 4;
            }
            if (value >= 10**2) {
                value /= 10**2;
                result += 2;
            }
            if (value >= 10**1) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log10(value);
            return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256, rounded down, of a positive value.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return result + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0);
        }
    }
}

// File: @openzeppelin/contracts/utils/Strings.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/Strings.sol)

pragma solidity ^0.8.0;

import "./math/Math.sol";

/**
 * @dev String operations.
 */
library Strings {
    bytes16 private constant _SYMBOLS = "0123456789abcdef";
    uint8 private constant _ADDRESS_LENGTH = 20;

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        unchecked {
            uint256 length = Math.log10(value) + 1;
            string memory buffer = new string(length);
            uint256 ptr;
            /// @solidity memory-safe-assembly
            assembly {
                ptr := add(buffer, add(32, length))
            }
            while (true) {
                ptr--;
                /// @solidity memory-safe-assembly
                assembly {
                    mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
                }
                value /= 10;
                if (value == 0) break;
            }
            return buffer;
        }
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        unchecked {
            return toHexString(value, Math.log256(value) + 1);
        }
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = _SYMBOLS[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }

    /**
     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
     */
    function toHexString(address addr) internal pure returns (string memory) {
        return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
    }
}

// File: contracts/Polly.sol



/*

       -*%%+++*#+:              .+     --
        =@*     *@*           -#@-  .=#%
        %@.      @@:          .@*    :@:
       -@*       @@.          =@.    #*  :=     --
       #@.      =@#  +*+#:    @=    =@: *=%*    *@
      .@*      :@#.-%-  =@   +%    .@*.+  +@    =#
      *@.    .+%= =%.   =@. :@-    *@     =@:   *-
     :@#---===-  -@-    #@  %%    :@-     -@-  .#
     #@:        .@*    .@+ +@:    ##      -@-  #.
    :@%         *@.    *% .@+    :@.      -@- =:
    #@-         @*    -@. +%  =. %+ .=    -@::=
   :@#          @=   -@- .@-==  +@-+-     -@-*
   *@-          #*  *#.  #@*.  :@@+       =@+
.:=++=:.         ===:    +:    :=.        +=
                                         +-
                                       =+.
                                  +*-=+.

v1

*/

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

import "@openzeppelin/contracts/proxy/Clones.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "./PollyModule.sol";
import "./PollyConfigurator.sol";
import "./PollyFeeHandler.sol";

/// @title Polly main contract
/// @author polly.tools

/**
 *
 * Polly is a modular smart contract framework that allows anyone to deploy registered modules as proxy contracts onchain.
 * The framework is built and designed by continuousengagement.xyz and is open source.
 */

contract Polly is Ownable {


    enum ModuleType {
        READONLY, CLONE
    }

    enum ParamType {
      UINT, INT, BOOL, STRING, ADDRESS
    }

    /// @dev struct for an uninstantiated module
    struct Module {
      string name;
      uint version;
      address implementation;
      bool clone;
    }

    /// @dev struct for an instantiated module
    struct ModuleInstance {
      string name;
      uint version;
      address location;
    }

    /// @dev struct for a module configuration
    struct Config {
      string name;
      string module;
      uint version;
      Param[] params;
    }

    /// @dev struct for a general type parameter
    struct Param {
      uint _uint;
      int _int;
      bool _bool;
      string _string;
      address _address;
    }


    /// PRIVATE PROPERTIES ///
    string[] private _module_names; // names of registered modules
    mapping(string => mapping(uint => address)) private _modules; // mapping of registered modules and their versions - name => (id => implementation)
    uint private _module_count; // the total number of registered modules
    mapping(string => uint) private _module_versions; // mapping of registered modules and their latest version - name => version
    mapping(address => mapping(uint => Config)) private _configs; // mapping of module configs - owner => (id => config)
    mapping(address => uint) private _configs_count; // mapping of owner config count - owner => count
    mapping(address => bool) private _configurators; // mapping of configurators - address => bool
    PollyFeeHandler private _fee_handler; // fee handler contract
    //////////////////



    /// EVENTS ///
    event moduleUpdated(
      string indexed indexedName, string name, uint version, address indexed implementation
    );

    event moduleCloned(
      string indexed indexedName, string name, uint version, address location
    );

    event moduleConfigured(
      string indexedName, string name, uint version, Polly.Param[] params
    );


    /// CONSTRUCTOR ///

    constructor() {
      _fee_handler = new PollyFeeHandler();
    }


    /// FEE ///
    function fee(address for_, uint value_) public view returns (uint) {
      return _fee_handler.get(for_, value_);
    }


    /// MODULES ///

    /// @dev adds or updates a given module implemenation
    /// @param implementation_ address of the module implementation
    function updateModule(address implementation_) public onlyOwner {

      string memory name_ = PollyModule(implementation_).PMNAME();
      uint version_ = PollyModule(implementation_).PMVERSION();

      require(_modules[name_][version_] == address(0), "MODULE_VERSION_EXISTS");
      require(version_ == _module_versions[name_]+1, "MODULE_VERSION_INVALID");

      _modules[name_][version_] = implementation_; // add module implementation address
      _module_versions[name_] = version_; // update module latest version

      if(version_ == 1)
        _module_names.push(name_); // This is a new module, add to module names mapping

      address configurator_ = getConfigurator(name_, version_); // get configurator address

      if(configurator_ != address(0))
        _configurators[configurator_] = true; // Store the configurator address for this module

      emit moduleUpdated(name_, name_, version_, implementation_); // emit event moduleUpated

    }


    /// @dev retrieves a specific module version base
    /// @param name_ string name of the module
    /// @param version_ uint version of the module
    /// @return address of the module implementation
    function getModule(string memory name_, uint version_) public view returns(Module memory){

      if(version_ < 1)
        version_ = getLatestModuleVersion(name_); // version_ is 0, get latest version

      require(moduleExists(name_, version_), string(abi.encodePacked('INVALID_MODULE_OR_VERSION: ', name_, '@', Strings.toString(version_))));

      Polly.ModuleType type_ = PollyModule(_modules[name_][version_]).PMTYPE(); // get module info from stored implementation
      bool clone_ = type_ == Polly.ModuleType.CLONE;

      return Module(name_, version_, _modules[name_][version_], clone_); // return module

    }


    /// @dev returns a list of modules available
    /// @param limit_ uint maximum number of modules to return
    /// @param page_ uint page of modules to return
    /// @param ascending_ bool sort modules ascending (true) or descending (false)
    /// @return Module[] array of modules
    function getModules(uint limit_, uint page_, bool ascending_) public view returns(Module[] memory){

      uint count_ = _module_names.length; // get total number of modules

      if(limit_ < 1 || limit_ > count_)
        limit_ = count_; // limit_ is 0, get all modules

      if(page_ < 1)
        page_ = 1; // page_ is 0, get first page

      uint i; // iterator
      uint index_; // index of module name in _module_names


      if(ascending_)
        index_ = page_ == 1 ? 0 : (page_-1)*limit_; // ascending, set index to last module result set
      else
        index_ = page_ == 1 ? count_ : count_ - (limit_*(page_-1)); // descending, set index to first module on result set


      if(
        (ascending_ && index_ >= count_) // ascending, index is greater than total number of modules
        || // or
        (!ascending_ && index_ == 0) // descending, index is 0
      )
        return new Module[](0); // no modules available - bail early


      Module[] memory modules_ = new Module[](limit_); // create array of modules

      if(ascending_){

        // ASCENDING
        while(index_ < limit_){
            modules_[i] = getModule(_module_names[index_], 0);
            ++i;
            ++index_;
        }

      }
      else {

        /// DESCENDING
        while(index_ > 0 && i < limit_){
            modules_[i] = getModule(_module_names[index_-1], 0);
            ++i;
            --index_;
        }

      }


      return modules_; // return modules

    }


    /// @dev retrieves the most recent version number for a module
    /// @param name_ string name of the module
    /// @return uint version number of the module
    function getLatestModuleVersion(string memory name_) public view returns(uint){
      return _module_versions[name_];
    }


    /// @dev check if a module version exists
    /// @param name_ string name of the module
    /// @param version_ uint version of the module
    /// @return exists_ bool true if module version exists
    function moduleExists(string memory name_, uint version_) public view returns(bool exists_){
      if(_modules[name_][version_] != address(0))
        exists_ = true;
      return exists_;
    }


    /// @dev get the configurator for a given module
    /// @param name_ string name of the module
    /// @param version_ uint version of the module
    /// @return address of the module configurator
    function getConfigurator(string memory name_, uint version_) public view returns(address){
      return PollyModule(_modules[name_][version_]).configurator();
    }

    /// @dev get the configurator fee for a given module
    /// @param name_ string name of the module
    /// @param version_ uint version of the module
    /// @return fee_ uint fee in points
    function getConfiguratorFee(address for_, string memory name_, uint version_, Polly.Param[] memory params_) public view returns(uint){

      address conf_address_ = getConfigurator(name_, version_);
      if(conf_address_ == address(0))
        return 0;
      return PollyConfigurator(conf_address_).fee(this, for_, params_);

    }


    /// @dev clone a given module
    /// @param name_ string name of the module
    /// @param version_ uint version of the module
    /// @return address of the cloned module implementation
    function cloneModule(string memory name_, uint version_) public returns(address) {

      if(version_ == 0)
        version_ = getLatestModuleVersion(name_); // version_ is 0, get latest version

      require(moduleExists(name_, version_), string(abi.encodePacked('INVALID_MODULE_OR_VERSION: ', name_, '@', Strings.toString(version_))));
      require(PollyModule(_modules[name_][version_]).PMTYPE() == Polly.ModuleType.CLONE, 'MODULE_NOT_CLONABLE'); // module is not clonable

      address implementation_ = _modules[name_][version_]; // get module implementation address

      PollyModule module_ = PollyModule(Clones.clone(implementation_)); // clone module implementation
      module_.init(msg.sender); // initialize module


      emit moduleCloned(name_, name_, version_, address(module_)); // emit module cloned event
      return address(module_); // return cloned module address

    }


    /// @dev if a module is configurable run the configurator
    /// @param name_ string name of the module
    /// @param version_ uint version of the module
    /// @param params_ array of configuration input parameters
    /// @return rparams_ array of configuration return parameters
    function configureModule(string memory name_, uint version_, Polly.Param[] memory params_, bool store_, string memory config_name_) public payable returns(Polly.Param[] memory rparams_) {

      if(version_ == 0)
        version_ = getLatestModuleVersion(name_); // version_ is 0, get latest version

      Module memory module_ = getModule(name_, version_); // get module
      address configurator_ = PollyModule(module_.implementation).configurator(); // get module configurator address
      require(configurator_ != address(0), 'NO_MODULE_CONFIGURATOR'); // module is not configurable - revert

      PollyConfigurator config_ = PollyConfigurator(configurator_); // get configurator instance

      // Fee
      uint fee_ = config_.fee(this, msg.sender, params_); // get configurator fee info
      if(!_configurators[msg.sender]){
        fee_ = fee_+_fee_handler.get(msg.sender, fee_);
      }

      require(fee_ == msg.value, 'INVALID_FEE');

      // Configure
      rparams_ = config_.run{value: fee_}(this, msg.sender, params_); // run configurator with params

      // Store
      if(store_){

        uint new_count_ = _configs_count[msg.sender] + 1; // get new config count for storing
        _configs[msg.sender][new_count_].name = config_name_; // store config name
        _configs[msg.sender][new_count_].module = name_; // store module name
        _configs[msg.sender][new_count_].version = version_; // store module name

        for (uint i = 0; i < rparams_.length; i++){ // store each config params
          _configs[msg.sender][new_count_].params.push(rparams_[i]);
        }

        _configs_count[msg.sender] = new_count_; // update config count

      }

      emit moduleConfigured(name_, name_, version_, rparams_); // emit module configured event
      return rparams_;  // return configuration params

    }

    /// @dev retrieves the stored configurations for a given address
    /// @param address_ address of the user
    /// @param limit_ maximum number of configurations to return
    /// @param page_ page of configurations to return
    /// @param ascending_ sort configurations ascending (true) or descending (false)
    /// @return PollyConfigurator.Config[] array of configurations
    function getConfigsForAddress(address address_, uint limit_, uint page_, bool ascending_) public view returns(Config[] memory){

      uint count_ = _configs_count[address_]; // get total number of configs for address

      if(limit_ < 1 || limit_ > count_)
        limit_ = count_;  // limit is 0 or greater than total number of configs, set limit to total number of configs

      if(page_ < 1)
        page_ = 1; // page is less than 1, set page to 1

      uint i; // counter
      uint id_; // config id

      if(ascending_)
        id_ = page_ == 1 ? 1 : ((page_-1)*limit_)+1; // calculate ascending start id
      else
        id_ = page_ == 1 ? count_ : count_ - (limit_*(page_-1)); // calculate descending start id


      if(
        (ascending_ && id_ > count_) // ascending and id is greater than total number of configs
        ||
        (!ascending_ && id_ == 0) // descending and id is 0
      )
        return new Config[](0); // no modules available - bail early


      Config[] memory configs_ = new Config[](limit_);  // create array of configs


      if(ascending_){

        // ASCENDING
        while(id_ <= count_ && i < limit_){
            configs_[i] = _configs[address_][id_];
            ++i;
            ++id_;
        }

      }
      else {

        /// DESCENDING
        while(id_ > 0 && i < limit_){
            configs_[i] = _configs[address_][id_];
            ++i;
            --id_;
        }

      }

      return configs_;

    }


    /// @dev retrieve a stored configuration for a given address and config index
    /// @param address_ address of the user
    /// @param index_ index of the configuration
    /// @return Polly.Config configuration
    function getConfigForAddress(address address_, uint index_) public view returns(Config memory){
      require(index_ > 0 && index_ <= _configs_count[address_], 'INVALID_CONFIG_INDEX'); // invalid config index
      return _configs[address_][index_]; // return config
    }


}

// File: contracts/PollyConfigurator.sol




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

import './Polly.sol';
import './PollyModule.sol';

abstract contract PollyConfigurator {

  string internal constant ADMIN = 'admin';
  string internal constant MANAGER = 'manager';

  function fee(Polly, address, Polly.Param[] memory) public view virtual returns(uint){return 0;}
  function inputs() external view virtual returns (string[] memory){return new string[](0);}
  function outputs() external view virtual returns (string[] memory){return new string[](0);}
  function run(Polly, address, Polly.Param[] memory) external virtual payable returns(Polly.Param[] memory){return new Polly.Param[](0);}

  function _transfer(address module_, address to_) internal {
    _grantAll(to_, module_);
    _revokeAll(address(this), module_);
  }

  function _grantManager(address to_, address module_) internal {
    PMClone m_ = PMClone(module_);
    m_.grantRole(MANAGER, to_);
  }

  function _grantAdmin(address to_, address module_) internal {
    PMClone m_ = PMClone(module_);
    m_.grantRole(ADMIN, to_);
  }

  function _grantAll(address to_, address module_) internal {
    PMClone m_ = PMClone(module_);
    m_.grantRole(ADMIN, to_);
    m_.grantRole(MANAGER, to_);
  }

  function _revokeManager(address to_, address module_) internal {
    PMClone m_ = PMClone(module_);
    m_.revokeRole(MANAGER, to_);
  }

  function _revokeAdmin(address to_, address module_) internal {
    PMClone m_ = PMClone(module_);
    m_.revokeRole(ADMIN, to_);
  }

  function _revokeAll(address to_, address module_) internal {
    PMClone m_ = PMClone(module_);
    m_.revokeRole(MANAGER, to_);
    m_.revokeRole(ADMIN, to_);
  }

}

// File: contracts/PollyFeeHandler.sol



// SPDX-License-Identifier: MIT

pragma solidity ^0.8.4;

contract PollyFeeHandler {

  /// @dev get the fee for a given address and amount
  function get(
    address,
    uint value_
  )
  public pure returns (uint){
    if(value_ == 0)
      return 0;
    return (value_/100)*5;
  }

}

// File: contracts/PollyModule.sol



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

import "@openzeppelin/contracts/proxy/Clones.sol";
import "@openzeppelin/contracts/utils/Strings.sol";
import "./Polly.sol";

/// @dev full interface for a PollyModule
interface PollyModule {

  function PMTYPE() external view returns(Polly.ModuleType);
  function PMNAME() external view returns(string memory);
  function PMVERSION() external view returns(uint);

  // Clonable
  function init(address for_) external;
  function didInit() external view returns(bool);
  function configurator() external view returns(address);
  function isManager(address address_) external view returns(bool);

  // Keystore
  function lockKey(string memory key_) external;
  function isLockedKey(string memory key_) external view returns(bool);
  function set(Polly.ParamType type_, string memory key_, Polly.Param memory value_) external;
  function get(string memory key_) external view returns(Polly.Param memory value_);

}


abstract contract PMBase {

  address private _configurator;

  function PMTYPE() external view virtual returns(Polly.ModuleType);
  function PMNAME() external view virtual returns(string memory);
  function PMVERSION() external view virtual returns(uint);

  // Configuration
  function _setConfigurator(address configurator_) internal {
    _configurator = configurator_;
  }

  function configurator() public view returns(address){
    return _configurator;
  }

}


abstract contract PMReadOnly is PMBase {

  Polly.ModuleType public constant override PMTYPE = Polly.ModuleType.READONLY;

}


abstract contract PMClone is PMBase {

  Polly.ModuleType public constant override PMTYPE = Polly.ModuleType.CLONE;

  bool private _did_init = false;

  address private _owner;
  mapping(address => mapping(string => bool)) private _roles;

  modifier onlyRole(string memory role_){
    require(hasRole(role_, msg.sender), string(abi.encodePacked('MISSING_ROLE: ', role_, ' - ', Strings.toHexString(uint160(msg.sender), 20))));
    _;
  }


  // Initialization
  constructor(){
    init(msg.sender);
  }

  function init(address for_) public virtual {
    require(!_did_init, 'ALREADY_INITIALIZED');
    _did_init = true;
    _grantRole('admin', for_);
    _grantRole('manager', for_);
  }

  function didInit() public view returns(bool){
    return _did_init;
  }


  /// Access control

  function _requireRole(string memory role_, address address_) internal view {
    require(hasRole(role_, address_), string(abi.encodePacked('MISSING_ROLE: ', role_, ' - ', Strings.toHexString(uint160(address_), 20))));
  }

  function owner() public view returns(address){
    return _owner;
  }

  function _setOwner(address new_owner_) internal {
    _owner = new_owner_;
    _grantRole('admin', new_owner_);
  }

  function setOwner(address new_owner_) public {
    _requireRole('admin', msg.sender);
    _setOwner(new_owner_);
  }

  function hasRole(string memory role_, address check_) public view returns(bool){
    return _roles[check_][role_];
  }

  function _grantRole(string memory role_, address to_) internal {
    _roles[to_][role_] = true;
  }

  function grantRole(string memory role_, address to_) public {
    _requireRole('admin', msg.sender);
    _grantRole(role_, to_);
  }

  function revokeRole(string memory role_, address to_) public {
    _requireRole('admin', msg.sender);
    _roles[to_][role_] = false;
  }

  function renounceRole(string memory role_) public {
    _requireRole(role_, msg.sender);
    _roles[msg.sender][role_] = false;
  }


}