ETH Price: $3,031.60 (+2.53%)
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To

There are no matching entries

Please try again later

View more zero value Internal Transactions in Advanced View mode

Advanced mode:

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Validator Index Block Amount
View All Withdrawals

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

Contract Source Code Verified (Exact Match)

Contract Name:
ETHKeys

Compiler Version
v0.8.15+commit.e14f2714

Optimization Enabled:
Yes with 200 runs

Other Settings:
london EvmVersion, MIT license
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import "./ERC20Shares.sol";
import "./ETHShares.sol";
import "./TradeableShares.sol";

/**
 * @title Tradeable Keys Alias
 *
 * @notice Displays the function names using the "keys" word instead of "shares"
 *      in MetaMask, Etherscan, etc.
 */
interface TradeableKeys is TradeableShares {
	/**
	 * @notice buyShares alias
	 */
	function buyKeys(uint256 amount) external payable;

	/**
	 * @notice buySharesTo alias
	 */
	function buyKeysTo(uint256 amount, address beneficiary) external payable;

	/**
	 * @notice sellShares alias
	 */
	function sellKeys(uint256 amount) external;

	/**
	 * @notice sellSharesTo alias
	 */
	function sellKeysTo(uint256 amount, address payable beneficiary) external;
}

/**
 * @title ERC20 Keys Alias
 *
 * @notice Displays the function names using the "keys" word instead of "shares"
 *      in MetaMask, Etherscan, etc.
 */
contract ERC20Keys is ERC20Shares, TradeableKeys {
	constructor(address _owner, SharesSubject memory _sharesSubject, address _protocolFeeDestination, uint64 _protocolFeePercent, HoldersRewardsDistributor _holdersFeeDestination, uint64 _holdersFeePercent, uint64 _subjectFeePercent, uint256 _amount, address _beneficiary, ERC1363 _paymentToken
	) ERC20Shares(      _owner,                      _sharesSubject,         _protocolFeeDestination,        _protocolFeePercent,                           _holdersFeeDestination,        _holdersFeePercent,        _subjectFeePercent,         _amount,         _beneficiary,         _paymentToken){}

	/**
	 * @inheritdoc TradeableKeys
	 */
	function buyKeys(uint256 amount) public payable {
		buyShares(amount);
	}

	/**
	 * @inheritdoc TradeableKeys
	 */
	function buyKeysTo(uint256 amount, address beneficiary) public payable {
		buySharesTo(amount, beneficiary);
	}

	/**
	 * @inheritdoc TradeableKeys
	 */
	function sellKeys(uint256 amount) public {
		sellShares(amount);
	}

	/**
	 * @inheritdoc TradeableKeys
	 */
	function sellKeysTo(uint256 amount, address payable beneficiary) public {
		sellSharesTo(amount, beneficiary);
	}
}

/**
 * @title ETH Keys Alias
 *
 * @notice Displays the function names using the "keys" word instead of "shares"
 *      in MetaMask, Etherscan, etc.
 */
contract ETHKeys is ETHShares, TradeableKeys {
	constructor(address _owner, SharesSubject memory _sharesSubject, address _protocolFeeDestination, uint64 _protocolFeePercent, HoldersRewardsDistributor _holdersFeeDestination, uint64 _holdersFeePercent, uint64 _subjectFeePercent, uint256 _amount, address _beneficiary
	) ETHShares(        _owner,                      _sharesSubject,         _protocolFeeDestination,        _protocolFeePercent,                           _holdersFeeDestination,        _holdersFeePercent,        _subjectFeePercent,         _amount,         _beneficiary){}

	/**
	 * @inheritdoc TradeableKeys
	 */
	function buyKeys(uint256 amount) public payable {
		buyShares(amount);
	}

	/**
	 * @inheritdoc TradeableKeys
	 */
	function buyKeysTo(uint256 amount, address beneficiary) public payable {
		buySharesTo(amount, beneficiary);
	}

	/**
	 * @inheritdoc TradeableKeys
	 */
	function sellKeys(uint256 amount) public {
		sellShares(amount);
	}

	/**
	 * @inheritdoc TradeableKeys
	 */
	function sellKeysTo(uint256 amount, address payable beneficiary) public {
		sellSharesTo(amount, beneficiary);
	}
}

File 2 of 21 : Initializable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (proxy/utils/Initializable.sol)

pragma solidity ^0.8.0;

import "../../utils/AddressUpgradeable.sol";

/**
 * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
 * behind a proxy. Since a proxied contract can't have a constructor, it's common to move constructor logic to an
 * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
 * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
 *
 * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
 * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
 *
 * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
 * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
 *
 * [CAUTION]
 * ====
 * Avoid leaving a contract uninitialized.
 *
 * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
 * contract, which may impact the proxy. To initialize the implementation contract, you can either invoke the
 * initializer manually, or you can include a constructor to automatically mark it as initialized when it is deployed:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * /// @custom:oz-upgrades-unsafe-allow constructor
 * constructor() initializer {}
 * ```
 * ====
 */
abstract contract Initializable {
    /**
     * @dev Indicates that the contract has been initialized.
     */
    bool private _initialized;

    /**
     * @dev Indicates that the contract is in the process of being initialized.
     */
    bool private _initializing;

    /**
     * @dev Modifier to protect an initializer function from being invoked twice.
     */
    modifier initializer() {
        // If the contract is initializing we ignore whether _initialized is set in order to support multiple
        // inheritance patterns, but we only do this in the context of a constructor, because in other contexts the
        // contract may have been reentered.
        require(_initializing ? _isConstructor() : !_initialized, "Initializable: contract is already initialized");

        bool isTopLevelCall = !_initializing;
        if (isTopLevelCall) {
            _initializing = true;
            _initialized = true;
        }

        _;

        if (isTopLevelCall) {
            _initializing = false;
        }
    }

    /**
     * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
     * {initializer} modifier, directly or indirectly.
     */
    modifier onlyInitializing() {
        require(_initializing, "Initializable: contract is not initializing");
        _;
    }

    function _isConstructor() private view returns (bool) {
        return !AddressUpgradeable.isContract(address(this));
    }
}

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

pragma solidity ^0.8.0;

/**
 * @dev Collection of functions related to the address type
 */
library AddressUpgradeable {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize, which returns 0 for contracts in
        // construction, since the code is only stored at the end of the
        // constructor execution.

        uint256 size;
        assembly {
            size := extcodesize(account)
        }
        return size > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCall(target, data, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        require(isContract(target), "Address: call to non-contract");

        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");

        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly

                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (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 Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        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);
    }
}

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

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

import "../utils/InitializableAccessControl.sol";
import "./TradeableShares.sol";
import "./SharesFactory.sol";
import "./SharesSubjectLib.sol";
import "./FriendTechBondingCurve.sol";

/**
 * @title Abstract Shares
 *
 * @notice Contains the logic which is currently common for the ETHShares
 *      and ERC20Shares TradeableShares implementations.
 *      Once these contracts diverge enough in their logic, this abstract contract
 *      may cease to exist.
 *
 * @dev Based on the friend.tech FriendtechSharesV1.sol
 */
abstract contract AbstractShares is TradeableShares, FriendTechBondingCurve, InitializableAccessControl {
	/// @dev Shares subject is an NFT; NFT owner receives the subject fee
	SharesSubject private sharesSubject;
	/// @dev Protocol fee destination is an address collecting the protocol fee
	address private protocolFeeDestination;
	/// @dev Protocol fee percent, immutable; maximum value: 10^18 (< 2^60)
	uint64 private /*immutable*/ protocolFeePercent;
	/// @dev Holders rewards fee destination is a contract collecting the holders fee, immutable
	HoldersRewardsDistributor private /*immutable*/ holdersFeeDestination;
	/// @dev Holders rewards fee percent, immutable
	uint64 private /*immutable*/ holdersFeePercent;
	/// @dev Subject fee percent, immutable
	uint64 private /*immutable*/ subjectFeePercent;

	/// @dev Total shares supply, sum of all the individual balances in `sharesBalances`
	uint256 internal sharesSupply;
	/// @dev Individual shares balances: Holder => Balance
	mapping(address => uint256) internal sharesBalances;

	/// @dev Cumulative value of all trades, allows to derive cumulative fees paid
	uint256 private tradeVolume;

	/**
	 * @dev Fired in `updateSharesSubject`
	 *
	 * @param oldSubject old shares subject
	 * @param newSubject new shares subject
	 * @param factory the factory contract notified about the update
	 */
	event SharesSubjectUpdated(SharesSubject oldSubject, SharesSubject newSubject, SharesFactory factory);

	/**
	 * @dev Fired in `updateProtocolFeeDestination`
	 *
	 * @param oldProtocolFeeDestination old protocol fee destination
	 * @param newProtocolFeeDestination new protocol fee destination
	 */
	event ProtocolFeeDestinationUpdated(address oldProtocolFeeDestination, address newProtocolFeeDestination);

	/**
	 * @dev Fire in `disableHoldersFee` no more than once
	 *      for the entire lifespan of the contract
	 *
	 * @param oldProtocolFeePercent old protocol fee percent
	 * @param newProtocolFeePercent new protocol fee percent, new >= old
	 */
	event HoldersFeeDisabled(uint256 oldProtocolFeePercent, uint256 newProtocolFeePercent);

	/**
	 * @notice Protocol fee destination manager is responsible for updating the address collecting the
	 *      protocol fee destination, that is `protocolFeeDestination`; the manager cannot update the fee percent
	 *
	 * @dev This role should be granted to the MultiSig, not to EOA and not to
	 *      RBAC managed smart contract, so that this functionality is not scalable;
	 *      this reduces the risk of misuse, and/or malicious use
	 *
	 * @dev Role ROLE_PROTOCOL_FEE_MANAGER is required to execute `updateProtocolFeeDestination` function
	 */
	uint32 public constant ROLE_PROTOCOL_FEE_MANAGER = 0x0001_0000;

	/**
	 * @notice Holders fee [disable] manager can disable the shares holders fee functionality;
	 *      the manager cannot enable it back
	 *
	 * @dev This role should be granted to the MultiSig, not to EOA and not to
	 *      RBAC managed smart contract, so that this functionality is not scalable;
	 *      this reduces the risk of misuse, and/or malicious use
	 *
	 * @dev Role ROLE_HOLDERS_FEE_MANAGER is required to execute `disableHoldersFee` function
	 */
	uint32 public constant ROLE_HOLDERS_FEE_MANAGER = 0x0002_0000;

	/**
	 * @notice Shares subject manager is responsible for updating the "shares subject"
	 *      in case of emergency, for example if underlying NFT was stolen
	 *
	 * @dev This role should be granted to the MultiSig, not to EOA and not to
	 *      RBAC managed smart contract, so that this functionality is not scalable;
	 *      this reduces the risk of misuse, and/or malicious use
	 *
	 * @dev Role ROLE_SHARES_SUBJECT_MANAGER is required to execute `updateSharesSubject` function
	 */
	uint32 public constant ROLE_SHARES_SUBJECT_MANAGER = 0x0008_0000;

	/**
	 * @dev "Constructor replacement" for initializable, must be execute during or immediately after deployment
	 *      see https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable#initializers
	 *
	 * @param _owner the address receiving all the RBAC permissions on the contract
	 * @param _sharesSubject shares subject, usually defined as NFT (ERC721 contract address + NFT ID)
	 * @param _protocolFeeDestination protocol fee destination, the address protocol fee is sent to
	 * @param _protocolFeePercent protocol fee percent, applied to all the buy and sell operations;
	 *      the fee percent is defined with the 18 decimals, 10^18 corresponds to 100%
	 * @param _holdersFeeDestination shares holders fee destination, the HoldersRewardsDistributor contract
	 *      the shares holders fee is sent to
	 * @param _holdersFeePercent shares holders fee percent, applied to all the buy and sell operations,
	 *      the fee percent is defined with the 18 decimals, 10^18 corresponds to 100%
	 * @param _subjectFeePercent subject fee percent, applied to all the buy and sell operations,
	 *      the fee percent is defined with the 18 decimals, 10^18 corresponds to 100%
	 */
	function _postConstruct(
		address _owner,
		SharesSubject memory _sharesSubject,
		address _protocolFeeDestination,
		uint64 _protocolFeePercent,
		HoldersRewardsDistributor _holdersFeeDestination,
		uint64 _holdersFeePercent,
		uint64 _subjectFeePercent
	) internal onlyInitializing {
		// execute parent initializer
		_postConstruct(_owner);
		// this initializer is called only from the factory, we do not verify the
		// validity of the inputs generated by the factory itself
		// if the factory goes buggy/malicious after the upgrade, all the
		// shares contracts deployed after should be considered invalid
		sharesSubject = _sharesSubject;
		protocolFeeDestination = _protocolFeeDestination;
		protocolFeePercent = _protocolFeePercent;
		holdersFeeDestination = _holdersFeeDestination;
		holdersFeePercent = _holdersFeePercent;
		subjectFeePercent = _subjectFeePercent;
	}

	/**
	 * @inheritdoc TradeableShares
	 */
	function getSharesSubject() public view returns(SharesSubject memory) {
		// read value from the storage
		return sharesSubject;
	}

	/**
	 * @notice Updates the shares subject
	 *
	 * @dev This is a restricted access function which should be accessible only from the
	 *      MultiSig wallet controlling the protocol, so that its usage is not scalable
	 *
	 * @param _sharesSubject new subject to set
	 */
	function updateSharesSubject(SharesSubject calldata _sharesSubject) public {
		// delegate to `updateSharesSubject` with the zero factory
		updateSharesSubject(_sharesSubject, SharesFactory(address(0)));
	}

	/**
	 * @notice Updates the shares subject and optionally notifies the factory about the update;
	 *      update fails if the factory notification fails
	 *
	 * @dev This is a restricted access function which should be accessible only from the
	 *      MultiSig wallet controlling the protocol, so that its usage is not scalable
	 *
	 * @param _sharesSubject new subject to set
	 * @param _factory shares factory contract to notify about the update, optional
	 *      if set to zero, the notification is not done
	 */
	function updateSharesSubject(SharesSubject calldata _sharesSubject, SharesFactory _factory) public {
		// verify the access permission
		require(isSenderInRole(ROLE_SHARES_SUBJECT_MANAGER), "access denied");

		// emit an event first - to log both old and new values
		emit SharesSubjectUpdated(sharesSubject, _sharesSubject, _factory);

		// update contract's state
		sharesSubject = _sharesSubject;

		// if factory is set (factory notification requested)
		if(address(_factory) != address(0)) {
			// notify factory contract
			_factory.notifySubjectUpdated();
		}
	}

	/**
	 * @inheritdoc TradeableShares
	 */
	function getProtocolFeeDestination() public view returns(address) {
		// read the value from storage
		return protocolFeeDestination;
	}

	/**
	 * @notice Updates the protocol fee destination address `protocolFeeDestination`
	 *
	 * @dev This is a restricted access function which should be accessible only from the
	 *      MultiSig wallet controlling the protocol, so that its usage is not scalable
	 *
	 * @param _protocolFeeDestination new protocol fee destination address to set
	 */
	function updateProtocolFeeDestination(address _protocolFeeDestination) public {
		// verify the access permission
		require(isSenderInRole(ROLE_PROTOCOL_FEE_MANAGER), "access denied");

		// emit an event first - to log both old and new values
		emit ProtocolFeeDestinationUpdated(protocolFeeDestination, _protocolFeeDestination);

		// update contract's state
		protocolFeeDestination = _protocolFeeDestination;
	}

	/**
	 * @inheritdoc TradeableShares
	 */
	function getProtocolFeePercent() public view returns(uint256) {
		// read the value from storage (immutable)
		return protocolFeePercent;
	}

	/**
	 * @inheritdoc TradeableShares
	 */
	function getProtocolFeeInfo() public view returns(address feeDestination, uint256 feePercent) {
		// read fee destination first
		feeDestination = getProtocolFeeDestination();
		// if it's zero, zero down the fee as well
		feePercent = feeDestination == address(0)? 0: getProtocolFeePercent();
	}

	/**
	 * @inheritdoc TradeableShares
	 */
	function getHoldersFeeDestination() public view returns(HoldersRewardsDistributor) {
		// read the value from storage (immutable)
		return holdersFeeDestination;
	}

	/**
	 * @notice Disables shares holders fee functionality; detaches shares contract from
	 *      the HoldersRewardsDistributor, stops sending fees, stops sending syncs
	 *
	 * @notice Increases the protocol fee by the value of the disabled shares holders fee,
	 *      so that the sum of all the fees remains the same
	 *
	 * @notice Once disabled, the holders fee functionality cannot be enabled back
	 */
	function disableHoldersFee() public {
		// verify the access permission
		require(isSenderInRole(ROLE_HOLDERS_FEE_MANAGER), "access denied");

		// verify the holders functionality is enabled
		require(address(holdersFeeDestination) != address(0) || holdersFeePercent != 0, "not enabled");

		// emit an event first - to log both old and new values
		emit HoldersFeeDisabled(protocolFeePercent, protocolFeePercent + holdersFeePercent);

		// shares holders fee goes to the protocol from now on
		protocolFeePercent += holdersFeePercent;

		// zero the shares holders fee
		holdersFeeDestination = HoldersRewardsDistributor(address(0));
		holdersFeePercent = 0;
	}

	/**
	 * @inheritdoc TradeableShares
	 */
	function getHoldersFeePercent() public view returns(uint256) {
		// read the value from storage (immutable)
		return holdersFeePercent;
	}

	/**
	 * @inheritdoc TradeableShares
	 */
	function getHoldersFeeInfo() public view returns(HoldersRewardsDistributor feeDestination, uint256 feePercent) {
		// read fee destination first
		feeDestination = getHoldersFeeDestination();
		// if it's zero, zero down the fee as well
		feePercent = address(feeDestination) == address(0)? 0: getHoldersFeePercent();
	}

	/**
	 * @inheritdoc TradeableShares
	 */
	function getSubjectFeeInfo() public view returns(address feeDestination, uint256 feePercent) {
		// read fee destination first
		feeDestination = getSharesIssuer();
		// if it's zero, zero down the fee as well
		feePercent = feeDestination == address(0)? 0: getSubjectFeePercent();
	}

	/**
	 * @inheritdoc TradeableShares
	 */
	function getSubjectFeePercent() public view returns(uint256) {
		// read the value from storage (immutable)
		return subjectFeePercent;
	}

	/**
	 * @inheritdoc TradeableShares
	 */
	function getSharesIssuer() public view returns(address nftOwner) {
		// derive the NFT owner defined by the subject
		return SharesSubjectLib.getSharesIssuer(sharesSubject);
	}

	/**
	 * @inheritdoc TradeableShares
	 */
	function getSharesBalance(address _holder) public view returns(uint256 balance) {
		// read the value from storage
		return sharesBalances[_holder];
	}

	/**
	 * @inheritdoc TradeableShares
	 */
	function getSharesSupply() public view returns(uint256 supply) {
		// read the value from storage
		return sharesSupply;
	}

	/**
	 * @inheritdoc TradeableShares
	 */
	function getBuyPrice(uint256 _supply, uint256 _amount) public pure returns(uint256) {
		// this is the original friend tech formula
		return getPrice(_supply, _amount);
	}

	/**
	 * @inheritdoc TradeableShares
	 */
	function getSellPrice(uint256 _supply, uint256 _amount) public pure returns(uint256) {
		// this is the original friend tech formula
		return getPrice(_supply - _amount, _amount);
	}

	/**
	 * @inheritdoc TradeableShares
	 */
	function getBuyPriceAfterFee(
		uint256 _supply,
		uint256 _amount,
		uint256 _protocolFeePercent,
		uint256 _holdersFeePercent,
		uint256 _subjectFeePercent
	) public pure returns(uint256) {
		// this is the original friend tech formula
		uint256 price = getBuyPrice(_supply, _amount);
		uint256 protocolFee = price * _protocolFeePercent / 1 ether;
		uint256 holdersFee = price * _holdersFeePercent / 1 ether;
		uint256 subjectFee = price * _subjectFeePercent / 1 ether;
		return price + protocolFee + holdersFee + subjectFee;
	}

	/**
	 * @inheritdoc TradeableShares
	 */
	function getSellPriceAfterFee(
		uint256 _supply,
		uint256 _amount,
		uint256 _protocolFeePercent,
		uint256 _holdersFeePercent,
		uint256 _subjectFeePercent
	) public pure returns(uint256) {
		// this is the original friend tech formula
		uint256 price = getSellPrice(_supply, _amount);
		uint256 protocolFee = price * _protocolFeePercent / 1 ether;
		uint256 holdersFee = price * _holdersFeePercent / 1 ether;
		uint256 subjectFee = price * _subjectFeePercent / 1 ether;
		return price - protocolFee - holdersFee - subjectFee;
	}

	/**
	 * @inheritdoc TradeableShares
	 */
	function getBuyPrice(uint256 _amount) public view returns(uint256) {
		// delegate to `getBuyPrice`
		return getBuyPrice(getSharesSupply(), _amount);
	}

	/**
	 * @inheritdoc TradeableShares
	 */
	function getSellPrice(uint256 _amount) public view returns(uint256) {
		// delegate to `getSellPrice`
		return getSellPrice(getSharesSupply(), _amount);
	}

	/**
	 * @inheritdoc TradeableShares
	 */
	function getBuyPriceAfterFee(uint256 _amount) public view returns(uint256) {
		// read the effective fees values
		(, uint256 _protocolFeePercent) = getProtocolFeeInfo();
		(, uint256 _holdersFeePercent) = getHoldersFeeInfo();
		(, uint256 _subjectFeePercent) = getSubjectFeeInfo();

		// delegate to `getBuyPriceAfterFee`
		return getBuyPriceAfterFee(getSharesSupply(), _amount, _protocolFeePercent, _holdersFeePercent, _subjectFeePercent);
	}

	/**
	 * @inheritdoc TradeableShares
	 */
	function getSellPriceAfterFee(uint256 _amount) public view returns(uint256) {
		// read the effective fees values
		(, uint256 _protocolFeePercent) = getProtocolFeeInfo();
		(, uint256 _holdersFeePercent) = getHoldersFeeInfo();
		(, uint256 _subjectFeePercent) = getSubjectFeeInfo();

		// delegate to `getSellPriceAfterFee`
		return getSellPriceAfterFee(getSharesSupply(), _amount, _protocolFeePercent, _holdersFeePercent, _subjectFeePercent);
	}

	/**
	 * @dev Executed internally on every trade (buy/sell) to track the trading volume
	 *
	 * @param value trading operation value, the price of the buy/sell operation without the fees
	 */
	function __increaseTradeVolume(uint256 value) internal {
		// update the value in the storage
		tradeVolume += value;
	}

	/**
	 * @inheritdoc TradeableShares
	 */
	function getTradeVolume() public view returns(uint256) {
		// read the value from the storage
		return tradeVolume;
	}
}

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

/**
 * @title Bonding Curve
 *
 * @notice A friend.tech-like bonding curve definition
 *
 * @notice Bonding curve defines the price of the smallest unit of the asset as a function
 *      of the asset supply
 */
interface BondingCurve {
	/**
	 * @notice Bonding curve function definition. The function calculating the price
	 *      of the `amount` of shares given the current total supply `supply`
	 *
	 * @param supply total shares supply
	 * @param amount number of shares to buy/sell
	 * @return the price of the shares (all `amount` amount)
	 */
	function getPrice(uint256 supply, uint256 amount) external pure returns(uint256);
}

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

import "../interfaces/ERC1363Spec.sol";
import "./AbstractShares.sol";

/**
 * @title ERC20 Shares
 *
 * @notice TradeableShares implementation using ERC20 token as a payment token
 *
 * @dev Doesn't have "payable" functions, that is the functions accepting ETH
 *
 * @dev Based on the friend.tech FriendtechSharesV1.sol
 */
contract ERC20Shares is AbstractShares {
	/// @dev ERC1363 payment token used for payments
	ERC1363 private /*immutable*/ paymentToken;

	/**
	 * @dev Deploys the ERC20Shares instance and initializes it
	 *
	 * @param _owner the address receiving all the RBAC permissions on the contract
	 * @param _sharesSubject shares subject, usually defined as NFT (ERC721 contract address + NFT ID)
	 * @param _protocolFeeDestination protocol fee destination, the address protocol fee is sent to
	 * @param _protocolFeePercent protocol fee percent, applied to all the buy and sell operations;
	 *      the fee percent is defined with the 18 decimals, 10^18 corresponds to 100%
	 * @param _holdersFeeDestination shares holders fee destination, the HoldersRewardsDistributor contract
	 *      the shares holders fee is sent to
	 * @param _holdersFeePercent shares holders fee percent, applied to all the buy and sell operations,
	 *      the fee percent is defined with the 18 decimals, 10^18 corresponds to 100%
	 * @param _subjectFeePercent subject fee percent, applied to all the buy and sell operations,
	 *      the fee percent is defined with the 18 decimals, 10^18 corresponds to 100%
	 * @param _amount how many shares to buy immediately upon "post-construction", can be zero
	 * @param _beneficiary the address receiving the shares bought immediately (must be set
	 *      if `_amount` is not zero)
	 * @param _paymentToken ERC1363 token used as a payment token instead of ETH
	 */
	constructor(
		address _owner,
		SharesSubject memory _sharesSubject,
		address _protocolFeeDestination,
		uint64 _protocolFeePercent,
		HoldersRewardsDistributor _holdersFeeDestination,
		uint64 _holdersFeePercent,
		uint64 _subjectFeePercent,
		uint256 _amount,
		address _beneficiary,
		ERC1363 _paymentToken
	) initializer {
		// initialize the deployed instance
		postConstruct(
			_owner,
			_sharesSubject,
			_protocolFeeDestination,
			_protocolFeePercent,
			_holdersFeeDestination,
			_holdersFeePercent,
			_subjectFeePercent,
			_amount,
			_beneficiary,
			_paymentToken
		);
	}

	/**
	 * @dev "Constructor replacement" for initializable, must be execute during or immediately after deployment
	 *      see https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable#initializers
	 *
	 * @param _owner the address receiving all the RBAC permissions on the contract
	 * @param _sharesSubject shares subject, usually defined as NFT (ERC721 contract address + NFT ID)
	 * @param _protocolFeeDestination protocol fee destination, the address protocol fee is sent to
	 * @param _protocolFeePercent protocol fee percent, applied to all the buy and sell operations;
	 *      the fee percent is defined with the 18 decimals, 10^18 corresponds to 100%
	 * @param _holdersFeeDestination shares holders fee destination, the HoldersRewardsDistributor contract
	 *      the shares holders fee is sent to
	 * @param _holdersFeePercent shares holders fee percent, applied to all the buy and sell operations,
	 *      the fee percent is defined with the 18 decimals, 10^18 corresponds to 100%
	 * @param _subjectFeePercent subject fee percent, applied to all the buy and sell operations,
	 *      the fee percent is defined with the 18 decimals, 10^18 corresponds to 100%
	 * @param _amount how many shares to buy immediately upon "post-construction", can be zero
	 * @param _beneficiary the address receiving the shares bought immediately (must be set
	 *      if `_amount` is not zero)
	 * @param _paymentToken ERC20 token used as a payment token instead of ETH
	 */
	function postConstruct(
		address _owner,
		SharesSubject memory _sharesSubject,
		address _protocolFeeDestination,
		uint64 _protocolFeePercent,
		HoldersRewardsDistributor _holdersFeeDestination,
		uint64 _holdersFeePercent,
		uint64 _subjectFeePercent,
		uint256 _amount,
		address _beneficiary,
		ERC1363 _paymentToken
	) public initializer {
		// execute parent initializer
		_postConstruct(
			_owner,
			_sharesSubject,
			_protocolFeeDestination,
			_protocolFeePercent,
			_holdersFeeDestination,
			_holdersFeePercent,
			_subjectFeePercent
		);
		// no need to check if payment token is zero since this is designed to be
		// deployed only from the factory where the ERC20 address in non-modifiable
		// and is defined on the deployment of the factory
		paymentToken = _paymentToken;

		// buy shares if requested
		if(_amount != 0) {
			__buySharesTo(_amount, _beneficiary);
		}
	}

	/**
	 * @notice ERC1363 payment token getter
	 *
	 * @return ERC1363 payment token, immutable
	 */
	function getPaymentToken() public view returns(ERC1363) {
		// read from the storage and return
		return paymentToken;
	}

	/**
	 * @inheritdoc BondingCurve
	 *
	 * @notice Shifts the curve by multiplying the result by 50,000
	 */
	function getPrice(uint256 supply, uint256 amount) public pure override(BondingCurve, FriendTechBondingCurve) returns(uint256) {
		// shift the curve by 50,000
		return (10**5 / 2) * super.getPrice(supply, amount);
	}

	/**
	 * @inheritdoc TradeableShares
	 */
	function buyShares(uint256 amount) public payable {
		// delegate to `buySharesTo`
		buySharesTo(amount, msg.sender);
	}

	/**
	 * @inheritdoc TradeableShares
	 */
	function sellShares(uint256 amount) public {
		// delegate to `sellSharesTo`
		sellSharesTo(amount, payable(msg.sender));
	}

	/**
	 * @inheritdoc TradeableShares
	 */
	function buySharesTo(uint256 amount, address beneficiary) public payable {
		// verify the first share is not bought
		require(getSharesSupply() > 0 || getSharesIssuer() == msg.sender, "only the issuer can buy the first share");

		// delegate to unsafe `__buySharesTo`
		__buySharesTo(amount, beneficiary);
	}

	/**
	 * @dev Buys amount of shares for the beneficiary, without checking if the first share was bought
	 *
	 * @param amount amount of the shares to buy
	 * @param beneficiary an address receiving the shares
	 */
	function __buySharesTo(uint256 amount, address beneficiary) private {
		// ERC20 implementation doesn't expect Ether to be sent
		require(msg.value == 0, "only payment in ERC20 token is expected");

		// cache the supply value
		uint256 supply = getSharesSupply();

		// update the balances (note: security checks are below)
		sharesBalances[beneficiary] += amount;
		sharesSupply = supply + amount;

		// determine the price and process the fees
		uint256 price = getPrice(supply, amount);
		(, , uint256 protocolFee) = __processProtocolFee(msg.sender, price);
		(, , uint256 holdersFee) = __processHoldersFeeAndNotify(msg.sender, price, true, amount, beneficiary);
		(address issuer, , uint256 subjectFee) = __processSubjectFee(msg.sender, price);

		// do the required ERC20 payment token price transfer
		require(
			// do not try to transfer zero price
			price == 0 || paymentToken.transferFrom(msg.sender, address(this), price),
			"payment failed"
		);

		// update the cumulative trade volume
		__increaseTradeVolume(price);

		// emit an event
		emit Trade(beneficiary, issuer, true, amount, price, protocolFee, holdersFee, subjectFee, sharesSupply);
	}

	/**
	 * @inheritdoc TradeableShares
	 */
	function sellSharesTo(uint256 amount, address payable beneficiary) public {
		// verify the amount vs total supply
		uint256 supply = getSharesSupply();
		require(supply > amount, "cannot sell the last share");

		// verify the amount vs seller's balance
		uint256 balance = getSharesBalance(msg.sender);
		require(balance >= amount, "insufficient shares");

		// update the balances
		sharesBalances[msg.sender] = balance - amount;
		sharesSupply = supply - amount;

		// determine the price and process the fees
		uint256 price = getPrice(sharesSupply, amount);
		(, , uint256 protocolFee) = __processProtocolFee(address(this), price);
		(, , uint256 holdersFee) = __processHoldersFeeAndNotify(address(this), price, false, amount, msg.sender);
		(address issuer, , uint256 subjectFee) = __processSubjectFee(address(this), price);

		// price cannot be zero since the last share cannot be sold
		// if the price transfer fails, we do fail
		// note: if any of the fees failed to transfer, they are sent to the seller
		require(
			paymentToken.transfer(beneficiary, price - protocolFee - holdersFee - subjectFee),
			"payment failed"
		);

		// update the cumulative trade volume
		__increaseTradeVolume(price);

		// emit an event
		emit Trade(beneficiary, issuer, false, amount, price, protocolFee, holdersFee, subjectFee, sharesSupply);
	}

	/**
	 * @dev Calculates the protocol fee and sends it to the protocol fee destination
	 *
	 * @param from the address where the tokens are being sent from, this can be either
	 *      the the shares buyer, or the contract itself (when selling)
	 * @param price already calculated price of the trade
	 */
	function __processProtocolFee(address from, uint256 price) private returns(
		address protocolFeeDestination,
		uint256 protocolFeePercent,
		uint256 protocolFee
	) {
		// read fee information in a consistent way
		(protocolFeeDestination, protocolFeePercent) = getProtocolFeeInfo();

		// calculate the fee
		protocolFee = price * protocolFeePercent / 1 ether;

		// do the required ERC20 payment token transfer
		require(
			// do not try to transfer zero protocol fee
			protocolFee == 0 || paymentToken.transferFrom(from, protocolFeeDestination, protocolFee),
			"protocol fee payment failed"
		);
	}

	/**
	 * @dev Calculates the shares holders fee and sends it to the holders fee destination;
	 *      notifies the destination (which is a HoldersRewardsDistributor contract) about
	 *      the trade, submits trader address, and trade amount
	 *
	 * @dev isBuy is true if the shares are bought
	 *      isBuy is false if the shares are sold
	 *
	 * @param from the address where the tokens are being sent from, this can be either
	 *      the the shares buyer, or the contract itself (when selling)
	 * @param price already calculated price of the trade
	 * @param isBuy operation type, [true] buying, [false] selling
	 * @param amount trade amount
	 * @param trader an account which makes a trade, whose shares balance changes by the `amount`
	 */
	function __processHoldersFeeAndNotify(address from, uint256 price, bool isBuy, uint256 amount, address trader) private returns(
		HoldersRewardsDistributor holdersFeeDestination,
		uint256 holdersFeePercent,
		uint256 holdersFee
	) {
		// read fee information in a consistent way
		(holdersFeeDestination, holdersFeePercent) = getHoldersFeeInfo();

		// calculate the fee
		holdersFee = price * holdersFeePercent / 1 ether;

		// do the required ERC1363 payment token transfer and HoldersRewardsDistributor sync
		if(address(holdersFeeDestination) != address(0) && amount != 0) {
			// construct the HoldersRewardsDistributor sync message
			bytes memory syncMessage = abi.encode(trader, isBuy, amount);

			// send the fee together with the sync message
			bool success = paymentToken.transferFromAndCall(from, address(holdersFeeDestination), holdersFee, syncMessage);

			// we require synchronization to succeed, otherwise we can't guarantee data consistency
			// on the HoldersRewardsDistributor contract's side
			require(success, "sync failed");
		}
	}

	/**
	 * @dev Calculates the subject fee and sends it to the issuer
	 *
	 * @param from the address where the tokens are being sent from, this can be either
	 *      the the shares buyer, or the contract itself (when selling)
	 * @param price already calculated price of the trade
	 */
	function __processSubjectFee(address from, uint256 price) private returns(
		address subjectFeeDestination,
		uint256 subjectFeePercent,
		uint256 subjectFee
	) {
		// read fee information in a consistent way
		(subjectFeeDestination, subjectFeePercent) = getSubjectFeeInfo();

		// calculate the fee
		subjectFee = price * subjectFeePercent / 1 ether;

		// do the required ERC20 payment token transfer
		require(
			// do not try to transfer zero subject fee
			subjectFee == 0 || paymentToken.transferFrom(from, subjectFeeDestination, subjectFee),
			"subject fee payment failed"
		);
	}
}

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

import "../utils/Transfers.sol";
import "./AbstractShares.sol";

/**
 * @title ETH Shares
 *
 * @notice TradeableShares implementation using native ETH for payments
 *
 * @dev Based on the friend.tech FriendtechSharesV1.sol
 */
contract ETHShares is AbstractShares {
	/// @dev Overrides standard send and transfer Solidity functions
	using Transfers for address payable;

	/**
	 * @dev Deploys the ETHShares instance and initializes it
	 *
	 * @param _owner the address receiving all the RBAC permissions on the contract
	 * @param _sharesSubject shares subject, usually defined as NFT (ERC721 contract address + NFT ID)
	 * @param _protocolFeeDestination protocol fee destination, the address protocol fee is sent to
	 * @param _protocolFeePercent protocol fee percent, applied to all the buy and sell operations;
	 *      the fee percent is defined with the 18 decimals, 10^18 corresponds to 100%
	 * @param _holdersFeeDestination shares holders fee destination, the HoldersRewardsDistributor contract
	 *      the shares holders fee is sent to
	 * @param _holdersFeePercent shares holders fee percent, applied to all the buy and sell operations,
	 *      the fee percent is defined with the 18 decimals, 10^18 corresponds to 100%
	 * @param _subjectFeePercent subject fee percent, applied to all the buy and sell operations,
	 *      the fee percent is defined with the 18 decimals, 10^18 corresponds to 100%
	 * @param _amount how many shares to buy immediately upon "post-construction", can be zero
	 * @param _beneficiary the address receiving the shares bought immediately (must be set
	 *      if `_amount` is not zero)
	 */
	constructor(
		address _owner,
		SharesSubject memory _sharesSubject,
		address _protocolFeeDestination,
		uint64 _protocolFeePercent,
		HoldersRewardsDistributor _holdersFeeDestination,
		uint64 _holdersFeePercent,
		uint64 _subjectFeePercent,
		uint256 _amount,
		address _beneficiary
	) payable initializer {
		// initialize the deployed instance
		postConstruct(
			_owner,
			_sharesSubject,
			_protocolFeeDestination,
			_protocolFeePercent,
			_holdersFeeDestination,
			_holdersFeePercent,
			_subjectFeePercent,
			_amount,
			_beneficiary
		);
	}

	/**
	 * @dev "Constructor replacement" for initializable, must be execute during or immediately after deployment
	 *      see https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable#initializers
	 *
	 * @param _owner the address receiving all the RBAC permissions on the contract
	 * @param _sharesSubject shares subject, usually defined as NFT (ERC721 contract address + NFT ID)
	 * @param _protocolFeeDestination protocol fee destination, the address protocol fee is sent to
	 * @param _protocolFeePercent protocol fee percent, applied to all the buy and sell operations;
	 *      the fee percent is defined with the 18 decimals, 10^18 corresponds to 100%
	 * @param _holdersFeeDestination shares holders fee destination, the HoldersRewardsDistributor contract
	 *      the shares holders fee is sent to
	 * @param _holdersFeePercent shares holders fee percent, applied to all the buy and sell operations,
	 *      the fee percent is defined with the 18 decimals, 10^18 corresponds to 100%
	 * @param _subjectFeePercent subject fee percent, applied to all the buy and sell operations,
	 *      the fee percent is defined with the 18 decimals, 10^18 corresponds to 100%
	 * @param _amount how many shares to buy immediately upon "post-construction", can be zero
	 * @param _beneficiary the address receiving the shares bought immediately (must be set
	 *      if `_amount` is not zero)
	 */
	function postConstruct(
		address _owner,
		SharesSubject memory _sharesSubject,
		address _protocolFeeDestination,
		uint64 _protocolFeePercent,
		HoldersRewardsDistributor _holdersFeeDestination,
		uint64 _holdersFeePercent,
		uint64 _subjectFeePercent,
		uint256 _amount,
		address _beneficiary
	) public payable initializer {
		// execute parent initializer
		_postConstruct(
			_owner,
			_sharesSubject,
			_protocolFeeDestination,
			_protocolFeePercent,
			_holdersFeeDestination,
			_holdersFeePercent,
			_subjectFeePercent
		);

		// buy shares if requested
		if(_amount != 0) {
			__buySharesTo(_amount, _beneficiary);
		}
		// otherwise if transaction contains a payment
		else if(msg.value > 0) {
			//  don't forget to return it back
			payable(msg.sender).transfer1(msg.value);
		}
	}

	/**
	 * @inheritdoc BondingCurve
	 *
	 * @notice Shifts the curve by dividing the result by 2
	 */
	function getPrice(uint256 supply, uint256 amount) public pure override(BondingCurve, FriendTechBondingCurve) returns(uint256) {
		// shift the curve by -2
		return super.getPrice(supply, amount) / 2;
	}

	/**
	 * @inheritdoc TradeableShares
	 */
	function buyShares(uint256 amount) public payable {
		// delegate to `buySharesTo`
		buySharesTo(amount, msg.sender);
	}

	/**
	 * @inheritdoc TradeableShares
	 */
	function sellShares(uint256 amount) public {
		// delegate to `sellSharesTo`
		sellSharesTo(amount, payable(msg.sender));
	}

	/**
	 * @inheritdoc TradeableShares
	 */
	function buySharesTo(uint256 amount, address beneficiary) public payable {
		// verify the first share is not bought
		require(getSharesSupply() > 0 || getSharesIssuer() == msg.sender, "only the issuer can buy the first share");

		// delegate to unsafe `__buySharesTo`
		__buySharesTo(amount, beneficiary);
	}

	/**
	 * @dev Buys amount of shares for the beneficiary, without checking if the first share was bought
	 *
	 * @param amount amount of the shares to buy
	 * @param beneficiary an address receiving the shares
	 */
	function __buySharesTo(uint256 amount, address beneficiary) private {
		// cache the supply value
		uint256 supply = getSharesSupply();

		// update the balances (note: security checks are below)
		sharesBalances[beneficiary] += amount;
		sharesSupply = supply + amount;

		// determine the price and process the fees
		uint256 price = getPrice(supply, amount);
		(, , uint256 protocolFee) = __processProtocolFee(price);
		(, , uint256 holdersFee) = __processHoldersFeeAndNotify(price, true, amount, beneficiary);
		(address issuer, , uint256 subjectFee) = __processSubjectFee(price);

		// verify the transaction has enough Ether supplied
		uint256 value = price + protocolFee + holdersFee + subjectFee;
		require(msg.value >= value, "insufficient value supplied");

		// return the change back to the buyer; here we do fail on error
		// note: if any of the fees failed to transfer, they are sent to the buyer
		if(msg.value > value) {
			payable(msg.sender).transfer1(msg.value - value);
		}

		// update the cumulative trade volume
		__increaseTradeVolume(price);

		// emit an event
		emit Trade(beneficiary, issuer, true, amount, price, protocolFee, holdersFee, subjectFee, sharesSupply);
	}

	/**
	 * @inheritdoc TradeableShares
	 */
	function sellSharesTo(uint256 amount, address payable beneficiary) public {
		// verify the amount vs total supply
		uint256 supply = getSharesSupply();
		require(supply > amount, "cannot sell the last share");

		// verify the amount vs seller's balance
		uint256 balance = getSharesBalance(msg.sender);
		require(balance >= amount, "insufficient shares");

		// update the balances
		sharesBalances[msg.sender] = balance - amount;
		sharesSupply = supply - amount;

		// determine the price and process the fees
		uint256 price = getPrice(sharesSupply, amount);
		(, , uint256 protocolFee) = __processProtocolFee(price);
		(, , uint256 holdersFee) = __processHoldersFeeAndNotify(price, false, amount, msg.sender);
		(address issuer, , uint256 subjectFee) = __processSubjectFee(price);

		// price cannot be zero since the last share cannot be sold
		// if the price transfer fails, we do fail
		// note: if any of the fees failed to transfer, they are sent to the seller
		beneficiary.transfer1(price - protocolFee - holdersFee - subjectFee);

		// update the cumulative trade volume
		__increaseTradeVolume(price);

		// emit an event
		emit Trade(beneficiary, issuer, false, amount, price, protocolFee, holdersFee, subjectFee, sharesSupply);
	}

	/**
	 * @dev Calculates the protocol fee and sends it to the protocol fee destination
	 *
	 * @param price already calculated price of the trade
	 */
	function __processProtocolFee(uint256 price) private returns(
		address protocolFeeDestination,
		uint256 protocolFeePercent,
		uint256 protocolFee
	) {
		// read fee information in a consistent way
		(protocolFeeDestination, protocolFeePercent) = getProtocolFeeInfo();

		// calculate the fee
		protocolFee = price * protocolFeePercent / 1 ether;

		// do the required ETH payment transfer
		// if the fee payment fails - do not throw and update the fee to zero
		if(protocolFee != 0 && !payable(protocolFeeDestination).send1(protocolFee)) {
			// protocol fee couldn't be sent or is zero
			protocolFee = 0;
		}
	}

	/**
	 * @dev Calculates the shares holders fee and sends it to the holders fee destination;
	 *      notifies the destination (which is a HoldersRewardsDistributor contract) about
	 *      the trade, submits trader address, and trade amount
	 *
	 * @dev isBuy is true if the shares are bought
	 *      isBuy is false if the shares are sold
	 *
	 * @param price already calculated price of the trade
	 * @param isBuy operation type, [true] buying, [false] selling
	 * @param amount trade amount
	 * @param trader an account which makes a trade, whose shares balance changes by the `amount`
	 */
	function __processHoldersFeeAndNotify(uint256 price, bool isBuy, uint256 amount, address trader) private returns(
		HoldersRewardsDistributor holdersFeeDestination,
		uint256 holdersFeePercent,
		uint256 holdersFee
	) {
		// read fee information in a consistent way
		(holdersFeeDestination, holdersFeePercent) = getHoldersFeeInfo();

		// calculate the fee
		holdersFee = price * holdersFeePercent / 1 ether;

		// do the required ETH payment transfer and HoldersRewardsDistributor sync
		if(address(holdersFeeDestination) != address(0) && amount != 0) {
			// construct the HoldersRewardsDistributor sync message
			bytes memory syncMessage = abi.encode(trader, isBuy, amount);

			// send the fee together with the sync message
			// we pass all the gas available since the fee destination address is trusted,
			// and since if the call fails we also fail the entire transaction
			(bool success, ) = address(holdersFeeDestination).call{value: holdersFee}(syncMessage);

			// we require synchronization to succeed, otherwise we can't guarantee data consistency
			// on the HoldersRewardsDistributor contract's side
			require(success, "sync failed");
		}
	}

	/**
	 * @dev Calculates the subject fee and sends it to the issuer
	 *
	 * @param price already calculated price of the trade
	 */
	function __processSubjectFee(uint256 price) private returns(
		address subjectFeeDestination,
		uint256 subjectFeePercent,
		uint256 subjectFee
	) {
		// read fee information in a consistent way
		(subjectFeeDestination, subjectFeePercent) = getSubjectFeeInfo();

		// calculate the fee
		subjectFee = price * subjectFeePercent / 1 ether;

		// do the required ETH payment transfer
		// if the fee payment fails - do not throw and update the fee to zero
		if(subjectFee != 0 && !payable(subjectFeeDestination).send1(subjectFee)) {
			// protocol fee couldn't be sent or is zero
			subjectFee = 0;
		}
	}
}

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

import "./BondingCurve.sol";

/**
 * @title FriendTech Bonding Curve
 *
 * @notice friend.tech definition of the bonding curve function
 */
contract FriendTechBondingCurve is BondingCurve {
	/**
	 * @inheritdoc BondingCurve
	 *
	 * @param s supply, total shares supply
	 * @param a amount, number of shares to buy/sell
	 */
	function getPrice(uint256 s, uint256 a) public pure virtual returns(uint256) {
		// this is the original friend tech formula with the underflow fix
		// the fix allows both supply and amount be zero, as well as
		// it allows supply be zero when the amount is bigger than one
		uint256 sum1 = s == 0 ? 0 : (s - 1) * s * (2 * (s - 1) + 1) / 6;
		uint256 sum2 = s == 0 && a <= 1 ? 0 : (s + a - 1) * (s + a) * (2 * (s + a - 1) + 1) / 6;
		uint256 summation = sum2 - sum1;
		return summation * 1 ether / 16000;
	}
}

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

import "../interfaces/ERC1363Spec.sol";

/**
 * @title Bonding Curve Holder Reward Distributor
 *
 * @notice Holder reward distributor keeps track of every trade event happening in the curve,
 *      and based on the amount of shares the holder has, alters the holders' reward weight,
 *      which directly affects the amount of the distributed rewards between the holders
 *
 * @notice Holder reward distributor accepts the fees from the curve and distributes these fees
 *      across shares holders proportionally to their weights
 *
 * @dev Apart from the `accept(uint256,address)` function designed to accept the fees from the
 *      curve contract, the implementation must implement receive(), fallback(), and onTransferReceived()
 *      functions to accept direct payments in both ETH and/or ERC20 payment token
 *
 * @dev receive() and onTransferReceived() with an empty data field must accept the fee in the same way
 *      as an accept() function would do, but in a passive way (without ERC20 transfer)
 *
 * @dev The fallback() and onTransferReceived() with non-empty data field must accept the fee and the trading event;
 *      trading event encoded in the bytes data field contains the information
 *      on the trade which resulted in the fee being sent:
 *
 *      - address trader - shares holder/trader
 *      - bool isBuy - true if shares were bought, false if shares were sold
 *      - uint256 sharesAmount - amount of shares bought or sold
 *
 *      the values above are packed as data = abi.encode(trader, isBuy, sharesAmount)
 *      and can be unpacked as (trader, isBuy, sharesAmount) = abi.decode(data, (address, bool, uint256))
 *
 *      if specified, the data field must be parsed by the implementation and its containing data applied;
 *      standard logic applies, if the data is malformed implementation should throw
 *
 */
interface HoldersRewardsDistributor is ERC1363Receiver {
	/**
	 * @dev Fired in `sharesBought` and `sharesSold`
	 *
	 * @param trader is a buyer or a seller, depending on the operation type
	 * @param isBuy true if the event comes from the `sharesBought` and represents the buy operation,
	 *      false if the event comes from the `sharesSold` and represents the sell operation
	 * @param sharesAmount amount of the shares bought or sold (see `isBuy`)
	 */
	event SharesTraded(address indexed trader, bool indexed isBuy, uint256 sharesAmount);

	/**
	 * @dev Fired when the fee for the distribution is received
	 *
	 * @param feeAmount amount of the fee to distribute between the holders
	 */
	event FeeReceived(uint256 feeAmount);

	/**
	 * @dev Fired in `claimReward`
	 *
	 * @param holder address of the trader (and shares holder) who received the reward
	 * @param rewardAmount amount of the reward sent
	 */
	event RewardClaimed(address indexed holder, uint256 rewardAmount);

	/**
	 * @notice ERC20 payment token distributor is bound to
	 *
	 * @return paymentToken ERC20 payment token address the contract is bound to,
	 *      or zero zero address if it operates with the plain ETH
	 */
	function getPaymentToken() external view returns(address paymentToken);

/*
	*/
/**
	 * @notice Notifies the distributor about the trade event
	 *
	 * @dev Trade amount specified affects holder's (buyer's) weight when calculating the reward
	 *
	 * @param buyer shares buyer (becomes shares holder if not yet), a.k.a trader
	 * @param amountBought amount of the shares bought
	 *//*

	function sharesBought(address buyer, uint256 amountBought) external;

	*/
/**
	 * @notice Notifies the distributor about the trade event
	 *
	 * @dev Trade amount specified affects holder's (seller's) weight when calculating the reward
	 *
	 * @param seller shares seller (shares holder), a.k.a trader
	 * @param amountSold amount of the shares sold
	 *//*

	function sharesSold(address seller, uint256 amountSold) external;

	*/
/**
	 * @notice Executed by the fee sender to send the fee; in case of the ERC20 payment,
	 *      this is the ask to take the specified amount of the ERC20 token of the specified type;
	 *      in case of the ETH payment, the amount must be supplied with the transaction itself
	 *
	 * @dev When paying with an ERC20 payment token, sender must approve the contract for
	 *      at least the amount specified before executing this function
	 *
	 * @dev Updates the accumulated reward per share
	 *
	 * @param feeAmount amount of the fee sent,
	 *      in the case of ETH payment must be equal to msg.value
	 *//*

	function accept(uint256 feeAmount) external payable;
*/

	/**
	 * @notice Executed by the holder to claim entire pending reward
	 *
	 * @dev Holder can verify pending reward amount with the `pendingReward` function
	 */
	function claimTheReward() external;

	/**
	 * @notice Pending (claimable) reward. This is the amount which can be claimed using `claimTheReward`
	 *
	 * @param holder the holder address to query the reward for
	 * @return rewardAmount pending reward amount\
	 */
	function pendingReward(address holder) external view returns(uint256 rewardAmount);
}

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

import "./TradeableShares.sol";

/**
 * @title Shares Factory
 *
 * @notice Creates/deploys TradeableShares contracts
 *
 * @notice The factory manages protocol fees of the deployed TradeableShares contract:
 *      deployed contracts usually follow the protocol fees set on the factory
 *
 * @dev Based on the friend.tech FriendtechSharesV1.sol
 */
interface SharesFactory {
	/**
	 * @dev Enum of all possible TradeableShares implementations the factory can deploy
	 */
	enum ImplementationType {
		/// @dev ETHShares implementation
		ETH,
		/// @dev ERC20Shares implementation bound to the ERC20 payment token
		ERC20
	}

	/**
	 * @dev Shares deployment request is used to enable the TradeableShares
	 *      deployment with meta-transactions
	 * @dev See `executeDeploymentRequest()`
	 */
	struct SharesDeploymentRequest {
		/// @dev TradeableShares implementation type
		ImplementationType implementationType;
		/// @dev shares subject, owner of the curve
		TradeableShares.SharesSubject sharesSubject;
		/// @dev an address to mint the NFT defined by the subject if it doesn't exist
		address issuer;
		/// @dev how many shares to buy immediately after the deployment
		uint256 amount;
		/// @dev unix timestamp when the request becomes valid
		uint256 validFromTimestamp;
		/// @dev unix timestamp when the request expires (becomes invalid)
		uint256 expiresAtTimestamp;
		/// @dev nonce of the request (sequential number, increased by one)
		uint256 nonce;
	}

	/**
	 * @dev Fired in
	 *      `setProtocolFeeDestination`
	 *      `setProtocolFeePercent`
	 *      `setHoldersFeePercent`
	 *      `setSubjectFeePercent`
	 *      `setProtocolFee`
	 *
	 * @param protocolFeeDestination address where the protocol fee is sent
	 * @param protocolFeePercent protocol fee percent, value 10^18 corresponds to 100%
	 * @param holdersFeePercent shares holders fee percent, value 10^18 corresponds to 100%
	 * @param subjectFeePercent subject fee percent, value 10^18 corresponds to 100%
	 */
	event ProtocolFeeUpdated(
		address protocolFeeDestination,
		uint64 protocolFeePercent,
		uint64 holdersFeePercent,
		uint64 subjectFeePercent
	);

	/**
	 * @dev Fired in `deploySharesContract` and `registerSharesContract`
	 *
	 * @param creator shares creator, a.k.a. shares issuer, or current owner
	 * @param implementationContract newly deployed or registered TradeableShares contract
	 * @param holdersRewardsDistributor the shares holders fee destination, HoldersRewardsDistributor contract,
	 *      this can be zero if shares contract is deployed without the shares holders fee distribution
	 * @param implementationType type of the TradeableShares, see ImplementationType
	 * @param sharesSubject current shares subject
	 * @param newDeployment true if the factory deployed this TradeableShares contract,
	 *      false if TradeableShares contract was already deployed and factory just registered it
	 */
	event SharesContractRegistered(
		address indexed creator,
		TradeableShares indexed implementationContract,
		HoldersRewardsDistributor indexed holdersRewardsDistributor,
		ImplementationType implementationType,
		TradeableShares.SharesSubject sharesSubject,
		bool newDeployment
	);

	/**
	 * @dev Fired in `executeDeploymentRequest` and in `rewind`
	 */
	event NonceUsed(address indexed issuer, uint256 nonce);

	/**
	 * @notice Address of the already deployed TradeableShares implementation
	 *      to be used by the factory to deploy the TradeableShares contracts EIP-1167 clones
	 *
	 * @param _implementationType TradeableShares implementation type
	 * @return the address of the already deployed TradeableShares implementation corresponding
	 *      to the given implementation type
	 */
	function getSharesImplAddress(ImplementationType _implementationType) external view returns(address);

	/**
	 * @notice Address of the already deployed HoldersRewardsDistributor implementation
	 *      to be used by the factory to deploy the HoldersRewardsDistributor contracts EIP-1167 clones
	 *
	 * @dev If the HoldersRewardsDistributor implementation is missing, the TradeableShares contract
	 *      can still be deployed, not being attached to the HoldersRewardsDistributor
	 *
	 * @param _implementationType TradeableShares implementation type
	 * @return the address of the already deployed HoldersRewardsDistributor implementation corresponding
	 *      to the given implementation type
	 */
	function getDistributorImplAddress(ImplementationType _implementationType) external view returns(address);

	/**
	 * @notice Protocol fee destination is the address receiving the protocol fee
	 *
	 * @return feeDestination protocol fee destination, address
	 */
	function getProtocolFeeDestination() external view returns(address feeDestination);

	/**
	 * @notice Protocol fee percent is the percentage of the buy/sell transaction volume
	 *      sent to the protocol fee destination
	 *
	 * @dev The value has 18 decimals, 100% is represented as 10^18
	 *
	 * @return feePercent protocol fee percent
	 */
	function getProtocolFeePercent() external view returns(uint256 feePercent);

	/**
	 * @notice Shares holders fee percent is the percentage of the buy/sell transaction volume
	 *      sent to the shares holders rewards distributor contract
	 *
	 * @dev The value has 18 decimals, 100% is represented as 10^18
	 *
	 * @return feePercent shares holders fee percent
	 */
	function getHoldersFeePercent() external view returns(uint256 feePercent);

	/**
	 * @notice Subject fee percent is the percentage of the buy/sell transaction volume
	 *      sent to the subject issuer
	 *
	 * @dev The value has 18 decimals, 100% is represented as 10^18
	 *
	 * @dev Implementation may return different values for different callers,
	 *      for example it can read SharesSubject from the caller TradeableShares contract
	 *      and dynamically determine the subject fee
	 *
	 * @return feePercent subject fee percent
	 */
	function getSubjectFeePercent() external view returns(uint256 feePercent);

	/**
	 * @notice Sets the protocol fee destination
	 *
	 * @dev Implementation must check the consistency of the protocol fee destination and percent
	 *      set by this and `setProtocolFeePercent` functions
	 *
	 * @param feeDestination protocol fee destination to set
	 */
	function setProtocolFeeDestination(address feeDestination) external;

	/**
	 * @notice Sets the protocol fee percent
	 *
	 * @dev Implementation must check the consistency of the protocol fee destination and percent
	 *      set by this and `setProtocolFeeDestination` functions
	 *
	 * @param feePercent protocol fee percent to set, examples: 10^18 is 100%, 10^17 is 10%
	 */
	function setProtocolFeePercent(uint64 feePercent) external;

	/**
	 * @notice Sets the shares holders fee percent
	 *
	 * @param feePercent shares holders fee percent to set, examples: 10^18 is 100%, 10^17 is 10%
	 */
	function setHoldersFeePercent(uint64 feePercent) external;

	/**
	 * @notice Sets the subject fee percent
	 *
	 * @param feePercent subject fee percent to set, examples: 10^18 is 100%, 10^17 is 10%
	 */
	function setSubjectFeePercent(uint64 feePercent) external;

	/**
	 * @notice Sets all the fees at once:
	 *      protocolFeeDestination
	 *      protocolFeePercent
	 *      holdersFeePercent
	 *      subjectFeePercent
	 *
	 * @param protocolFeeDestination protocol fee destination to set
	 * @param protocolFeePercent protocol fee percent to set, examples: 10^18 is 100%, 10^17 is 10%
	 * @param holdersFeePercent shares holders fee percent to set, examples: 10^18 is 100%, 10^17 is 10%
	 * @param subjectFeePercent subject fee percent to set, examples: 10^18 is 100%, 10^17 is 10%
	 */
	function setProtocolFee(
		address protocolFeeDestination,
		uint64 protocolFeePercent,
		uint64 holdersFeePercent,
		uint64 subjectFeePercent
	) external;

	/**
	 * @notice Deploys the TradeableShares implementation for the specified subject;
	 *      the curve remains paused, no shares are being bought immediately
	 *
	 * @notice Tries minting the NFT defined by the subject if it doesn't exist
	 *
	 * @dev Implementation must guarantee only one TradeableShares contract per subject
	 *
	 * @param implementationType TradeableShares implementation type
	 * @param sharesSubject shares subject, owner of the curve
	 * @return deployed TradeableShares contract
	 */
	function deploySharesContractPaused(
		ImplementationType implementationType,
		TradeableShares.SharesSubject calldata sharesSubject
	) external returns(TradeableShares);

	/**
	 * @notice Deploys the TradeableShares implementation for the specified subject;
	 *      the curve launches immediately, the first share is issued to the subject issuer (NFT owner)
	 *
	 * @notice Tries minting the NFT defined by the subject if it doesn't exist
	 *
	 * @dev Implementation must guarantee only one TradeableShares contract per subject
	 *
	 * @param implementationType TradeableShares implementation type
	 * @param sharesSubject shares subject, owner of the curve
	 * @return deployed TradeableShares contract
	 */
	function deploySharesContract(
		ImplementationType implementationType,
		TradeableShares.SharesSubject calldata sharesSubject
	) external returns(TradeableShares);

	/**
	 * @notice Deploys the TradeableShares implementation for the specified subject;
	 *      allows to immediately buy any amount of shares (including zero)
	 *
	 * @notice Tries minting the NFT defined by the subject if it doesn't exist
	 *
	 * @dev Implementation must guarantee only one TradeableShares contract per subject
	 *
	 * @param implementationType TradeableShares implementation type
	 * @param sharesSubject shares subject, owner of the curve
	 * @param amount how many shares to buy immediately after the deployment
	 * @return deployed TradeableShares contract
	 */
	function deploySharesContractAndBuy(
		ImplementationType implementationType,
		TradeableShares.SharesSubject calldata sharesSubject,
		uint256 amount
	) external payable returns(TradeableShares);

	/**
	 * @notice Deploys the TradeableShares implementation for the specified subject;
	 *      allows to immediately buy any amount of shares (including zero)
	 *
	 * @notice Tries minting the NFT defined by the subject if it doesn't exist
	 *
	 * @dev Implementation must guarantee only one TradeableShares contract per subject
	 *
	 * @param implementationType TradeableShares implementation type
	 * @param sharesSubject shares subject, owner of the curve
	 * @param issuer an address to mint the NFT defined by the subject if it doesn't exist
	 * @param amount how many shares to buy immediately after the deployment
	 * @return deployed TradeableShares contract
	 */
	function mintSubjectAndDeployShares(
		ImplementationType implementationType,
		TradeableShares.SharesSubject calldata sharesSubject,
		address issuer,
		uint256 amount
	) external payable returns(TradeableShares);

	/**
	 * @notice Executes signed SharesDeploymentRequest; this is identical to executing `mintSubjectAndDeployShares`
	 *      on behalf of the signer and allows the transaction to be relayed so that the gas is payed by the
	 *      relayer
	 *
	 * @param req the deployment request to fulfill, containing same data as in `mintSubjectAndDeployShares`
	 * @param signature the deployment request EIP712 signature issued by the address allowed to execute
	 *      the request
	 * @return deployed TradeableShares contract
	 */
	function executeDeploymentRequest(
		SharesDeploymentRequest calldata req,
		bytes calldata signature
	) external payable returns(TradeableShares);

	/**
	 * @notice Gets current (unused) nonce for the given issuer address;
	 *      unused nonce is required to build the SharesDeploymentRequest and sign it
	 *      nonces increment by one after each use
	 *
	 * @param issuer the issuer address to get the nonce for
	 * @return current (unused) nonce; incremented by one after
	 *      each successful execution of the `executeDeploymentRequest` function
	 */
	function getNonce(address issuer) external view returns(uint256);

	/**
	 * @notice Rewinds forward the nonce for the issuer specified, used to
	 *      discard one or more signed requests to `executeDeploymentRequest`
	 *
	 * @dev Implementation must not allow to decrease the nonce, only increasing (rewinding)
	 *      must be possible
	 *
	 * @param issuer the issuer address to rewind the nonce for
	 * @param nonce the nonce value to rewind to
	 */
	function rewindNonce(address issuer, uint256 nonce) external;

	/**
	 * @notice Gets the already deployed TradeableShares contract
	 *
	 * @param sharesSubject shares subject, owner of the curve
	 * @return deployed TradeableShares contract
	 */
	function lookupSharesContract(
		TradeableShares.SharesSubject calldata sharesSubject
	) external view returns(TradeableShares);

	/**
	 * @notice Registers or re-registers the already deployed TradeableShares contract
	 *
	 * @dev Initial registration is usually done manually by authorized address,
	 *      Re-registration is usually done by the shares contract itself
	 *      and implementations must keep the access to this function open for
	 *      the already registered contracts
	 *
	 * @param shares already deployed TradeableShares contract
	 */
	function registerSharesContract(TradeableShares shares) external;

	/**
	 * @notice Executed only by the previously registered TradeableShares contracts
	 *      to notify the factory about the subject change.
	 *
	 * @dev The factory may throw if the subject is already taken by another contract
	 */
	function notifySubjectUpdated() external;
}

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

import "../interfaces/ERC721Spec.sol";
import "../interfaces/ERC721SpecExt.sol";
import "./TradeableShares.sol";

import "@openzeppelin/contracts/access/Ownable.sol";

/**
 * @title Shares Subject Library
 *
 * @notice Auxiliary functions to work with SharesSubject struct
 */
library SharesSubjectLib {
	/**
	 * @notice Determines current owner of the shares subject, which is a corresponding NFT owner
	 *
	 * @dev This function returns zero address if NFT doesn't exist, or even if NFT contract doesn't exist
	 *
	 * @param sharesSubject shares subject, owner of the curve
	 * @return address of the issuer, underlying NFT owner; or zero address
	 */
	function getSharesIssuer(TradeableShares.SharesSubject memory sharesSubject) internal view returns(address) {
		// we have to check if the address is callable, otherwise staticall would throw
		if(isCallable(sharesSubject.tokenAddress)) {
			// try to avoid an exception / failed call in the ownerOf function by checking NFT existence first
			// this is required *only* to avoid "partially failed" transaction display on etherscan
			{
				// we use staticcall instead of ABI function call to guaranty immutable call
				(bool success, bytes memory data) = sharesSubject.tokenAddress.staticcall{gas: 4900}(
					// MintableERC721 interface: function exists(uint256) external view returns(bool)
					abi.encodeWithSelector(MintableERC721.exists.selector, sharesSubject.tokenId)
				);
				// only if the call was successful
				if(success) {
					// try to decode the result as a bool,
					// and if we know for sure token doesn't exist,
					if(!abi.decode(data, (bool))) {
						// just return zero address as a default result in case of any error
						return address(0);
					}
				}
			}

			// try to get the ERC721 owner of the underlying NFT
			{
				// we use staticcall instead of ABI function call to guaranty immutable call
				(bool success, bytes memory data) = sharesSubject.tokenAddress.staticcall{gas: 4900}(
					// ERC721 interface: function ownerOf(uint256) external view returns(address)
					abi.encodeWithSelector(ERC721.ownerOf.selector, sharesSubject.tokenId)
				);
				// only if the call was successful
				if(success) {
					// try to decode the result as an address and return
					return abi.decode(data, (address));
				}
			}
		}

		// return the default zero address value in case of any errors
		return address(0);
	}

	/**
	 * @notice Determines the owner of the shares subject's underlying NFT collection
	 *
	 * @dev This function returns zero address if the underlying ERC721 contract is not OZ ownable
	 *      (doesn't have `owner()` function), doesn't exist, or if any other error occurs
	 *
	 * @param sharesSubject shares subject, owner of the curve
	 * @return address of the NFT collection owner (OZ ownable); or zero address
	 */
	function getCollectionOwner(TradeableShares.SharesSubject memory sharesSubject) internal view returns(address) {
		// we have to check if the address is callable, otherwise staticall would throw
		if(isCallable(sharesSubject.tokenAddress)) {
			// try to derive the owner via the OZ Ownable interface owner()
			// we use staticcall instead of ABI function call to guaranty immutable call
			(bool success, bytes memory data) = sharesSubject.tokenAddress.staticcall{gas: 4900}(
				// OZ Ownable interface: function owner() external view returns(address)
				abi.encodeWithSelector(Ownable.owner.selector)
			);

			// only if the call was successful
			if(success) {
				// try to decode the result as an address and return
				return abi.decode(data, (address));
			}
		}

		// return the default zero address value in case of any errors
		return address(0);
	}

	/**
	 * @notice Calculates the keccak256 bytes32 key for the shares subject to be used in the mappings
	 *
	 * @param sharesSubject shares subject, owner of the curve
	 * @return keccak256 of the shares subject
	 */
	function getSharesKey(TradeableShares.SharesSubject memory sharesSubject) internal pure returns(bytes32) {
		// delegate to `getSharesKey`
		return getSharesKey(sharesSubject.tokenAddress, sharesSubject.tokenId);
	}

	/**
	 * @notice Calculates the keccak256 bytes32 key for the shares subject to be used in the mappings
	 *
	 * @param tokenAddress shares subject token address (NFT address)
	 * @param tokenId shares subject token ID (NFT ID)
	 * @return keccak256 of the shares subject
	 */
	function getSharesKey(address tokenAddress, uint256 tokenId) internal pure returns(bytes32) {
		// calculate the keccak256 from the concatenated internals of the SharesSubject struct
		return keccak256(abi.encode(tokenAddress, tokenId));
	}

	/**
	 * @notice Checks if two subjects - subject 1 and subject 2 - are equal
	 *      Returns false if any of the subjects is not initialized (have zero ERC721 address)
	 *
	 * @param sharesSubject1 subject 1
	 * @param sharesSubject2 subject 2
	 * @return true if subject 1 and subject 2 are equal
	 */
	function equals(
		TradeableShares.SharesSubject memory sharesSubject1,
		TradeableShares.SharesSubject memory sharesSubject2
	) internal pure returns(bool) {
		return sharesSubject1.tokenAddress != address(0)
			&& sharesSubject1.tokenAddress == sharesSubject2.tokenAddress
			&& sharesSubject1.tokenId == sharesSubject2.tokenId;
	}

	/**
	 * @notice Verifies if the shares subject contains a value; this function is useful
	 *      to check if the value in storage (mapping) was initialized
	 *
	 * @param sharesSubject the shares subject to check
	 * @return true if the subject has a value, false otherwise (zero value)
	 */
	function isZero(TradeableShares.SharesSubject memory sharesSubject) internal pure returns(bool) {
		return sharesSubject.tokenAddress == address(0) && sharesSubject.tokenId == 0;
	}

	/**
	 * @notice Checks if account can be called (is callable, already deployed contract)
	 *
	 * @dev Verifies if the bytecode on the specified address is present
	 *
	 * @param account an address to check
	 * @return true if address denotes already deployed callable contract
	 */
	function isCallable(address account) internal view returns(bool) {
		// This method relies on extcodesize, which returns 0 for contracts in
		// construction, since the code is only stored at the end of the
		// constructor execution.

		uint256 size;
		assembly {
			size := extcodesize(account)
		}
		return size > 0;
	}
}

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

import "./BondingCurve.sol";
import "./HoldersRewardsDistributor.sol";

/**
 * @title Tradeable Shares
 *
 * @notice Tradeable shares is a non-transferable, but buyable/sellable fungible token-like asset,
 *      which is sold/bought solely by the shares contract at the predefined by
 *      the bonding curve function price
 *
 * @notice The shares is bound to its "subject" – an NFT; the NFT owner gets the subject fee
 *      emerging in every buy/sell operation
 *
 * @dev Based on the friend.tech FriendtechSharesV1.sol
 */
interface TradeableShares is BondingCurve {
	/**
	 * @notice Shares subject is an NFT defined by its ERC721 contract address and NFT ID
	 *       Note: this is different from the original FriendTech implementation where
	 *       shares subject is always equal to the issuer address
	 */
	struct SharesSubject {
		/// @dev ERC721 contract address
		address tokenAddress;

		/// @dev NFT ID
		uint256 tokenId;
	}

	/**
	 * @dev Fired in `buyShares` and `sellShares` functions, this event logs
	 *      the entire trading activity happening on the curve
	 *
	 * @dev Trader, that is the buyer or seller, depending on the operation type is the transaction sender
	 *
	 * @param beneficiary the address which receives shares or funds, usually this is the trader itself
	 * @param issuer subject issuer, usually an owner of the NFT defined by the subject
	 * @param isBuy true if the event comes from the `buyShares` and represents the buy operation,
	 *      false if the event comes from the `sellShares` and represents the sell operation
	 * @param sharesAmount amount of the shares bought or sold (see `isBuy`)
	 * @param paidAmount amount of ETH spent or gained by the buyer or seller;
	 *      this is implementation dependent and can represent an amount of ERC20 payment token
	 * @param protocolFeeAmount amount of fees paid to the protocol
	 * @param holdersFeeAmount amount of fees paid to the shares holders
	 * @param subjectFeeAmount amount of fees paid to the subject (issuer)
	 * @param supply total shares supply after the operation
	 */
	event Trade(
		address indexed beneficiary,
		address indexed issuer,
		bool indexed isBuy,
		uint256 sharesAmount,
		uint256 paidAmount,
		uint256 protocolFeeAmount,
		uint256 holdersFeeAmount,
		uint256 subjectFeeAmount,
		uint256 supply
	);

	/**
	 * @notice Shares subject, usually defined as NFT (ERC721 contract address + NFT ID)
	 *
	 * @dev Immutable, client applications may cache this value
	 *
	 * @return Shares subject as a SharesSubject struct, this is an NFT if all currently known implementations
	 */
	function getSharesSubject() external view returns(SharesSubject calldata);

	/**
	 * @notice Protocol fee destination, the address protocol fee is sent to
	 *
	 * @dev Mutable, can be changed by the protocol fee manager
	 *
	 * @return the address where the protocol fee is sent to
	 */
	function getProtocolFeeDestination() external view returns(address);

	/**
	 * @notice Protocol fee percent, applied to all the buy and sell operations;
	 *      the fee percent is defined with the 18 decimals, 10^18 corresponds to 100%
	 *
	 * @notice Protocol fee is sent to the protocol fee destination (see `getProtocolFeeDestination`)
	 *
	 * @dev Immutable, client applications may cache this value
	 *
	 * @return protocol fee percent with the 18 decimals (10^18 is 100%)
	 */
	function getProtocolFeePercent() external view returns(uint256);

	/**
	 * @notice Protocol fee destination and protocol fee percent as a tuple;
	 *      the fee percent is defined with the 18 decimals, 10^18 corresponds to 100%
	 *
	 * @dev Implementation must always return zero fee percent if fee destination is zero address
	 *
	 * @return feeDestination protocol fee destination
	 * @return feePercent protocol fee percent, zero if protocol fee destination is zero
	 */
	function getProtocolFeeInfo() external view returns(address feeDestination, uint256 feePercent);

	/**
	 * @notice Shares holders reward distributor contract attached to the shares contract
	 *      in order to receive its portion of the fees to be distributed among the shares holders
	 *
	 * @dev Immutable, client applications may cache this value; holders fee destination is not
	 *      an arbitrary address capable of receiving ETH or ERC20, but a HoldersRewardsDistributor
	 *      smart contract, which not only receives the fees but also receives updated on the
	 *      trading activity in the shares contract
	 *
	 * @return the contract where the holders fee is sent to
	 */
	function getHoldersFeeDestination() external view returns(HoldersRewardsDistributor);

	/**
	 * @notice Shares holders fee percent, applied to all the buy and sell operations;
	 *      the fee percent is defined with the 18 decimals, 10^18 corresponds to 100%
	 *
	 * @notice Shares holders fee is sent to the holders fee destination (see `getHoldersFeeDestination`)
	 *
	 * @dev Immutable, client applications may cache this value
	 *
	 * @return shares holders fee percent with the 18 decimals (10^18 is 100%)
	 */
	function getHoldersFeePercent() external view returns(uint256);

	/**
	 * @notice Shares holders fee destination and shares holders fee percent as a tuple;
	 *      the fee percent is defined with the 18 decimals, 10^18 corresponds to 100%
	 *
	 * @dev Implementation must always return zero fee percent if fee destination is zero
	 *
	 * @return feeDestination shares holders fee destination
	 * @return feePercent shares holders fee percent, zero if holders fee destination is zero
	 */
	function getHoldersFeeInfo() external view returns(HoldersRewardsDistributor feeDestination, uint256 feePercent);

	/**
	 * @notice Subject fee destination and subject fee percent as a tuple;
	 *      subject fee destination is shares issuer address;
	 *      the fee percent is defined with the 18 decimals, 10^18 corresponds to 100%;
	 *
	 * @dev Implementation must always return zero fee percent if fee destination is zero address
	 *
	 * @return feeDestination protocol fee destination
	 * @return feePercent protocol fee percent, zero if subject fee destination is zero
	 */
	function getSubjectFeeInfo() external view returns(address feeDestination, uint256 feePercent);

	/**
	 * @notice Subject fee percent, applied to all the buy and sell operations,
	 *      the fee percent is defined with the 18 decimals, 10^18 corresponds to 100%
	 *
	 * @notice Subject fee is sent to the subject fee issuer (see `getSharesIssuer`)
	 *
	 * @dev Immutable, client applications may cache this value
	 *
	 * @return subject fee percent with the 18 decimals (10^18 is 100%)
	 */
	function getSubjectFeePercent() external view returns(uint256);

	/**
	 * @notice Shares issuer, the receiver of the shares fees
	 *
	 * @dev Mutable, changes (potentially frequently and unpredictably) when the NFT owner changes;
	 *      subject to the front-run attacks, off-chain client applications must not rely on this address
	 *      in anyway
	 *
	 * @return nftOwner subject issuer, the owner of the NFT
	 */
	function getSharesIssuer() external view returns(address nftOwner);

	/**
	 * @notice Shares balance of the given holder; this function is similar to ERC20.balanceOf()
	 *
	 * @param holder the address to check the balance for
	 *
	 * @return balance number of shares the holder has
	 */
	function getSharesBalance(address holder) external view returns(uint256 balance);

	/**
	 * @notice Total amount of the shares in existence, the sum of all individual shares balances;
	 *      this function is similar to ERC20.totalSupply()
	 *
	 * @return supply total shares supply
	 */
	function getSharesSupply() external view returns(uint256 supply);

	/**
	 * @notice The price of the `amount` of shares to buy calculated based on
	 *      the specified total shares supply
	 *
	 * @param supply total shares supply
	 * @param amount number of shares to buy
	 * @return the price of the shares to buy
	 */
	function getBuyPrice(uint256 supply, uint256 amount) external pure returns(uint256);

	/**
	 * @notice The price of the `amount` of shares to sell calculated based on
	 *      the specified total shares supply
	 *
	 * @param supply total shares supply
	 * @param amount number of shares to sell
	 * @return the price of the shares to sell
	 */
	function getSellPrice(uint256 supply, uint256 amount) external pure returns(uint256);

	/**
	 * @notice The price of the `amount` of shares to buy, including all fees;
	 *      calculated based on the specified total shares supply and fees percentages
	 *
	 * @param supply total shares supply
	 * @param amount number of shares to buy
	 * @param protocolFeePercent protocol fee percent
	 * @param holdersFeePercent shares holders fee percent
	 * @param subjectFeePercent protocol fee percent
	 * @return the price of the shares to buy
	 */
	function getBuyPriceAfterFee(
		uint256 supply,
		uint256 amount,
		uint256 protocolFeePercent,
		uint256 holdersFeePercent,
		uint256 subjectFeePercent
	) external pure returns(uint256);

	/**
	 * @notice The price of the `amount` of shares to sell, including all fees;
	 *      calculated based on the specified total shares supply and fees percentages
	 *
	 * @param supply total shares supply
	 * @param amount number of shares to sell
	 * @param protocolFeePercent protocol fee percent
	 * @param holdersFeePercent shares holders fee percent
	 * @param subjectFeePercent protocol fee percent
	 * @return the price of the shares to sell
	 */
	function getSellPriceAfterFee(
		uint256 supply,
		uint256 amount,
		uint256 protocolFeePercent,
		uint256 holdersFeePercent,
		uint256 subjectFeePercent
	) external pure returns(uint256);

	/**
	 * @notice Current price of the `amount` of shares to buy; calculated based on
	 *      the current total shares supply
	 *
	 * @param amount number of shares to buy
	 * @return the price of the shares to buy
	 */
	function getBuyPrice(uint256 amount) external view returns(uint256);

	/**
	 * @notice Current price of the `amount` of shares to sell; calculated based on
	 *      the current total shares supply
	 *
	 * @param amount number of shares to sell
	 * @return the price of the shares to sell
	 */
	function getSellPrice(uint256 amount) external view returns(uint256);

	/**
	 * @notice Current price of the `amount` of shares to buy, including all fees;
	 *      calculated based on the current total shares supply and fees percentages
	 *
	 * @param amount number of shares to buy
	 * @return the price of the shares to buy
	 */
	function getBuyPriceAfterFee(uint256 amount) external view returns(uint256);

	/**
	 * @notice Current price of the `amount` of shares to sell, including all fees;
	 *      calculated based on the current total shares supply and fees percentages
	 *
	 * @param amount number of shares to sell
	 * @return the price of the shares to sell
	 */
	function getSellPriceAfterFee(uint256 amount) external view returns(uint256);

	/**
	 * @notice Buy `amount` of shares. Sender has to supply `getBuyPriceAfterFee(amount)` ETH.
	 *      First share can be bought only by current subject issuer.
	 *
	 * @dev Depending on the implementation, ERC20 token payment may be required instead of ETH.
	 *      In such a case, implementation must through if ETH is sent, effectively overriding
	 *      the function definition as non-payable
	 *
	 * @param amount amount of the shares to buy
	 */
	function buyShares(uint256 amount) external payable;

	/**
	 * @notice Buy `amount` of shares in the favor of the address specified (beneficiary).
	 *      Sender has to supply `getBuyPriceAfterFee(amount)` ETH.
	 *      First share can be bought only by current subject issuer.
	 *
	 * @dev Depending on the implementation, ERC20 token payment may be required instead of ETH.
	 *      In such a case, implementation must through if ETH is sent, effectively overriding
	 *      the function definition as non-payable
	 *
	 * @param amount amount of the shares to buy
	 * @param beneficiary an address receiving the shares
	 */
	function buySharesTo(uint256 amount, address beneficiary) external payable;

	/**
	 * @notice Sell `amount` of shares. Sender gets `getSellPriceAfterFee(amount)` of ETH.
	 *      Last share cannot be sold.
	 *
	 * @dev Depending on the implementation, ERC20 token may be payed instead of ETH.
	 *
	 * @param amount amount of the shares to sell
	 */
	function sellShares(uint256 amount) external;

	/**
	 * @notice Sell `amount` of shares in the favor of the address specified (beneficiary).
	 *      The beneficiary gets `getSellPriceAfterFee(amount)` of ETH.
	 *      Last share cannot be sold.
	 *
	 * @dev Depending on the implementation, ERC20 token may be payed instead of ETH.
	 *
	 * @param amount amount of the shares to sell
	 * @param beneficiary an address receiving the funds from the sale
	 */
	function sellSharesTo(uint256 amount, address payable beneficiary) external;

	/**
	 * @notice Cumulative value of all trades; allows to derive cumulative fees paid
	 *
	 * @dev This value cannot decrease over time; it can increase or remain constant
	 *      if no trades are happening
	 *
	 * @return Sum of the modulo of all trading operations
	 */
	function getTradeVolume() external view returns(uint256);
}

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

import "./ERC20Spec.sol";
import "./ERC165Spec.sol";

/**
 * @title ERC1363 Interface
 *
 * @dev Interface defining a ERC1363 Payable Token contract.
 *      Implementing contracts MUST implement the ERC1363 interface as well as the ERC20 and ERC165 interfaces.
 */
interface ERC1363 is ERC20, ERC165  {
	/*
	 * Note: the ERC-165 identifier for this interface is 0xb0202a11.
	 * 0xb0202a11 ===
	 *   bytes4(keccak256('transferAndCall(address,uint256)')) ^
	 *   bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^
	 *   bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^
	 *   bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) ^
	 *   bytes4(keccak256('approveAndCall(address,uint256)')) ^
	 *   bytes4(keccak256('approveAndCall(address,uint256,bytes)'))
	 */

	/**
	 * @notice Transfer tokens from `msg.sender` to another address and then call `onTransferReceived` on receiver
	 * @param to address The address which you want to transfer to
	 * @param value uint256 The amount of tokens to be transferred
	 * @return true unless throwing
	 */
	function transferAndCall(address to, uint256 value) external returns (bool);

	/**
	 * @notice Transfer tokens from `msg.sender` to another address and then call `onTransferReceived` on receiver
	 * @param to address The address which you want to transfer to
	 * @param value uint256 The amount of tokens to be transferred
	 * @param data bytes Additional data with no specified format, sent in call to `to`
	 * @return true unless throwing
	 */
	function transferAndCall(address to, uint256 value, bytes memory data) external returns (bool);

	/**
	 * @notice Transfer tokens from one address to another and then call `onTransferReceived` on receiver
	 * @param from address The address which you want to send tokens from
	 * @param to address The address which you want to transfer to
	 * @param value uint256 The amount of tokens to be transferred
	 * @return true unless throwing
	 */
	function transferFromAndCall(address from, address to, uint256 value) external returns (bool);


	/**
	 * @notice Transfer tokens from one address to another and then call `onTransferReceived` on receiver
	 * @param from address The address which you want to send tokens from
	 * @param to address The address which you want to transfer to
	 * @param value uint256 The amount of tokens to be transferred
	 * @param data bytes Additional data with no specified format, sent in call to `to`
	 * @return true unless throwing
	 */
	function transferFromAndCall(address from, address to, uint256 value, bytes memory data) external returns (bool);

	/**
	 * @notice Approve the passed address to spend the specified amount of tokens on behalf of msg.sender
	 * and then call `onApprovalReceived` on spender.
	 * @param spender address The address which will spend the funds
	 * @param value uint256 The amount of tokens to be spent
	 */
	function approveAndCall(address spender, uint256 value) external returns (bool);

	/**
	 * @notice Approve the passed address to spend the specified amount of tokens on behalf of msg.sender
	 * and then call `onApprovalReceived` on spender.
	 * @param spender address The address which will spend the funds
	 * @param value uint256 The amount of tokens to be spent
	 * @param data bytes Additional data with no specified format, sent in call to `spender`
	 */
	function approveAndCall(address spender, uint256 value, bytes memory data) external returns (bool);
}

/**
 * @title ERC1363Receiver Interface
 *
 * @dev Interface for any contract that wants to support `transferAndCall` or `transferFromAndCall`
 *      from ERC1363 token contracts.
 */
interface ERC1363Receiver {
	/*
	 * Note: the ERC-165 identifier for this interface is 0x88a7ca5c.
	 * 0x88a7ca5c === bytes4(keccak256("onTransferReceived(address,address,uint256,bytes)"))
	 */

	/**
	 * @notice Handle the receipt of ERC1363 tokens
	 *
	 * @dev Any ERC1363 smart contract calls this function on the recipient
	 *      after a `transfer` or a `transferFrom`. This function MAY throw to revert and reject the
	 *      transfer. Return of other than the magic value MUST result in the
	 *      transaction being reverted.
	 *      Note: the token contract address is always the message sender.
	 *
	 * @param operator address The address which called `transferAndCall` or `transferFromAndCall` function
	 * @param from address The address which are token transferred from
	 * @param value uint256 The amount of tokens transferred
	 * @param data bytes Additional data with no specified format
	 * @return `bytes4(keccak256("onTransferReceived(address,address,uint256,bytes)"))`
	 *      unless throwing
	 */
	function onTransferReceived(address operator, address from, uint256 value, bytes memory data) external returns (bytes4);
}

/**
 * @title ERC1363Spender Interface
 *
 * @dev Interface for any contract that wants to support `approveAndCall`
 *      from ERC1363 token contracts.
 */
interface ERC1363Spender {
	/*
	 * Note: the ERC-165 identifier for this interface is 0x7b04a2d0.
	 * 0x7b04a2d0 === bytes4(keccak256("onApprovalReceived(address,uint256,bytes)"))
	 */

	/**
	 * @notice Handle the approval of ERC1363 tokens
	 *
	 * @dev Any ERC1363 smart contract calls this function on the recipient
	 *      after an `approve`. This function MAY throw to revert and reject the
	 *      approval. Return of other than the magic value MUST result in the
	 *      transaction being reverted.
	 *      Note: the token contract address is always the message sender.
	 *
	 * @param owner address The address which called `approveAndCall` function
	 * @param value uint256 The amount of tokens to be spent
	 * @param data bytes Additional data with no specified format
	 * @return `bytes4(keccak256("onApprovalReceived(address,uint256,bytes)"))`
	 *      unless throwing
	 */
	function onApprovalReceived(address owner, uint256 value, bytes memory data) external returns (bytes4);
}

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

/**
 * @title ERC-165 Standard Interface Detection
 *
 * @dev Interface of the ERC165 standard, as defined in the
 *       https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * @dev Implementers can declare support of contract interfaces,
 *      which can then be queried by others.
 *
 * @author Christian Reitwießner, Nick Johnson, Fabian Vogelsteller, Jordi Baylina, Konrad Feldmeier, William Entriken
 */
interface ERC165 {
	/**
	 * @notice Query if a contract implements an interface
	 *
	 * @dev Interface identification is specified in ERC-165.
	 *      This function uses less than 30,000 gas.
	 *
	 * @param interfaceID The interface identifier, as specified in ERC-165
	 * @return `true` if the contract implements `interfaceID` and
	 *      `interfaceID` is not 0xffffffff, `false` otherwise
	 */
	function supportsInterface(bytes4 interfaceID) external view returns (bool);
}

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

/**
 * @title EIP-20: ERC-20 Token Standard
 *
 * @notice The ERC-20 (Ethereum Request for Comments 20), proposed by Fabian Vogelsteller in November 2015,
 *      is a Token Standard that implements an API for tokens within Smart Contracts.
 *
 * @notice It provides functionalities like to transfer tokens from one account to another,
 *      to get the current token balance of an account and also the total supply of the token available on the network.
 *      Besides these it also has some other functionalities like to approve that an amount of
 *      token from an account can be spent by a third party account.
 *
 * @notice If a Smart Contract implements the following methods and events it can be called an ERC-20 Token
 *      Contract and, once deployed, it will be responsible to keep track of the created tokens on Ethereum.
 *
 * @notice See https://ethereum.org/en/developers/docs/standards/tokens/erc-20/
 * @notice See https://eips.ethereum.org/EIPS/eip-20
 */
interface ERC20 {
	/**
	 * @dev Fired in transfer(), transferFrom() to indicate that token transfer happened
	 *
	 * @param from an address tokens were consumed from
	 * @param to an address tokens were sent to
	 * @param value number of tokens transferred
	 */
	event Transfer(address indexed from, address indexed to, uint256 value);

	/**
	 * @dev Fired in approve() to indicate an approval event happened
	 *
	 * @param owner an address which granted a permission to transfer
	 *      tokens on its behalf
	 * @param spender an address which received a permission to transfer
	 *      tokens on behalf of the owner `_owner`
	 * @param value amount of tokens granted to transfer on behalf
	 */
	event Approval(address indexed owner, address indexed spender, uint256 value);

	/**
	 * @return name of the token (ex.: USD Coin)
	 */
	// OPTIONAL - This method can be used to improve usability,
	// but interfaces and other contracts MUST NOT expect these values to be present.
	// function name() external view returns (string memory);

	/**
	 * @return symbol of the token (ex.: USDC)
	 */
	// OPTIONAL - This method can be used to improve usability,
	// but interfaces and other contracts MUST NOT expect these values to be present.
	// function symbol() external view returns (string memory);

	/**
	 * @dev Returns the number of decimals used to get its user representation.
	 *      For example, if `decimals` equals `2`, a balance of `505` tokens should
	 *      be displayed to a user as `5,05` (`505 / 10 ** 2`).
	 *
	 * @dev Tokens usually opt for a value of 18, imitating the relationship between
	 *      Ether and Wei. This is the value {ERC20} uses, unless this function is
	 *      overridden;
	 *
	 * @dev NOTE: This information is only used for _display_ purposes: it in
	 *      no way affects any of the arithmetic of the contract, including
	 *      {IERC20-balanceOf} and {IERC20-transfer}.
	 *
	 * @return token decimals
	 */
	// OPTIONAL - This method can be used to improve usability,
	// but interfaces and other contracts MUST NOT expect these values to be present.
	// function decimals() external view returns (uint8);

	/**
	 * @return the amount of tokens in existence
	 */
	function totalSupply() external view returns (uint256);

	/**
	 * @notice Gets the balance of a particular address
	 *
	 * @param _owner the address to query the the balance for
	 * @return balance an amount of tokens owned by the address specified
	 */
	function balanceOf(address _owner) external view returns (uint256 balance);

	/**
	 * @notice Transfers some tokens to an external address or a smart contract
	 *
	 * @dev Called by token owner (an address which has a
	 *      positive token balance tracked by this smart contract)
	 * @dev Throws on any error like
	 *      * insufficient token balance or
	 *      * incorrect `_to` address:
	 *          * zero address or
	 *          * self address or
	 *          * smart contract which doesn't support ERC20
	 *
	 * @param _to an address to transfer tokens to,
	 *      must be either an external address or a smart contract,
	 *      compliant with the ERC20 standard
	 * @param _value amount of tokens to be transferred,, zero
	 *      value is allowed
	 * @return success true on success, throws otherwise
	 */
	function transfer(address _to, uint256 _value) external returns (bool success);

	/**
	 * @notice Transfers some tokens on behalf of address `_from' (token owner)
	 *      to some other address `_to`
	 *
	 * @dev Called by token owner on his own or approved address,
	 *      an address approved earlier by token owner to
	 *      transfer some amount of tokens on its behalf
	 * @dev Throws on any error like
	 *      * insufficient token balance or
	 *      * incorrect `_to` address:
	 *          * zero address or
	 *          * same as `_from` address (self transfer)
	 *          * smart contract which doesn't support ERC20
	 *
	 * @param _from token owner which approved caller (transaction sender)
	 *      to transfer `_value` of tokens on its behalf
	 * @param _to an address to transfer tokens to,
	 *      must be either an external address or a smart contract,
	 *      compliant with the ERC20 standard
	 * @param _value amount of tokens to be transferred,, zero
	 *      value is allowed
	 * @return success true on success, throws otherwise
	 */
	function transferFrom(address _from, address _to, uint256 _value) external returns (bool success);

	/**
	 * @notice Approves address called `_spender` to transfer some amount
	 *      of tokens on behalf of the owner (transaction sender)
	 *
	 * @dev Transaction sender must not necessarily own any tokens to grant the permission
	 *
	 * @param _spender an address approved by the caller (token owner)
	 *      to spend some tokens on its behalf
	 * @param _value an amount of tokens spender `_spender` is allowed to
	 *      transfer on behalf of the token owner
	 * @return success true on success, throws otherwise
	 */
	function approve(address _spender, uint256 _value) external returns (bool success);

	/**
	 * @notice Returns the amount which _spender is still allowed to withdraw from _owner.
	 *
	 * @dev A function to check an amount of tokens owner approved
	 *      to transfer on its behalf by some other address called "spender"
	 *
	 * @param _owner an address which approves transferring some tokens on its behalf
	 * @param _spender an address approved to transfer some tokens on behalf
	 * @return remaining an amount of tokens approved address `_spender` can transfer on behalf
	 *      of token owner `_owner`
	 */
	function allowance(address _owner, address _spender) external view returns (uint256 remaining);
}

/**
 * @title Mintable/burnable ERC20 Extension
 *
 * @notice Adds mint/burn functions to ERC20 interface, these functions
 *      are usually present in ERC20 implementations, but these become
 *      a must for the bridged tokens in L2 since the bridge on L2
 *      needs to have a way to mint tokens deposited from L1 to L2
 *      and to burn tokens to be withdrawn from L2 to L1
 */
interface MintableBurnableERC20 is ERC20 {
	/**
	 * @dev Mints (creates) some tokens to address specified
	 * @dev The value specified is treated as is without taking
	 *      into account what `decimals` value is
	 *
	 * @param _to an address to mint tokens to
	 * @param _value an amount of tokens to mint (create)
	 */
	function mint(address _to, uint256 _value) external;

	/**
	 * @dev Burns (destroys) some tokens from the address specified
	 *
	 * @dev The value specified is treated as is without taking
	 *      into account what `decimals` value is
	 *
	 * @param _from an address to burn some tokens from
	 * @param _value an amount of tokens to burn (destroy)
	 */
	function burn(address _from, uint256 _value) external;
}

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

import "./ERC165Spec.sol";

/**
 * @title ERC-721 Non-Fungible Token Standard
 *
 * @notice See https://eips.ethereum.org/EIPS/eip-721
 *
 * @dev Solidity issue #3412: The ERC721 interfaces include explicit mutability guarantees for each function.
 *      Mutability guarantees are, in order weak to strong: payable, implicit nonpayable, view, and pure.
 *      Implementation MUST meet the mutability guarantee in this interface and MAY meet a stronger guarantee.
 *      For example, a payable function in this interface may be implemented as nonpayable
 *      (no state mutability specified) in implementing contract.
 *      It is expected a later Solidity release will allow stricter contract to inherit from this interface,
 *      but current workaround is that we edit this interface to add stricter mutability before inheriting:
 *      we have removed all "payable" modifiers.
 *
 * @dev The ERC-165 identifier for this interface is 0x80ac58cd.
 *
 * @author William Entriken, Dieter Shirley, Jacob Evans, Nastassia Sachs
 */
interface ERC721 is ERC165 {
	/// @dev This emits when ownership of any NFT changes by any mechanism.
	///  This event emits when NFTs are created (`from` == 0) and destroyed
	///  (`to` == 0). Exception: during contract creation, any number of NFTs
	///  may be created and assigned without emitting Transfer. At the time of
	///  any transfer, the approved address for that NFT (if any) is reset to none.
	event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);

	/// @dev This emits when the approved address for an NFT is changed or
	///  reaffirmed. The zero address indicates there is no approved address.
	///  When a Transfer event emits, this also indicates that the approved
	///  address for that NFT (if any) is reset to none.
	event Approval(address indexed _owner, address indexed _approved, uint256 indexed _tokenId);

	/// @dev This emits when an operator is enabled or disabled for an owner.
	///  The operator can manage all NFTs of the owner.
	event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);

	/// @notice Count all NFTs assigned to an owner
	/// @dev NFTs assigned to the zero address are considered invalid, and this
	///  function throws for queries about the zero address.
	/// @param _owner An address for whom to query the balance
	/// @return The number of NFTs owned by `_owner`, possibly zero
	function balanceOf(address _owner) external view returns (uint256);

	/// @notice Find the owner of an NFT
	/// @dev NFTs assigned to zero address are considered invalid, and queries
	///  about them do throw.
	/// @param _tokenId The identifier for an NFT
	/// @return The address of the owner of the NFT
	function ownerOf(uint256 _tokenId) external view returns (address);

	/// @notice Transfers the ownership of an NFT from one address to another address
	/// @dev Throws unless `msg.sender` is the current owner, an authorized
	///  operator, or the approved address for this NFT. Throws if `_from` is
	///  not the current owner. Throws if `_to` is the zero address. Throws if
	///  `_tokenId` is not a valid NFT. When transfer is complete, this function
	///  checks if `_to` is a smart contract (code size > 0). If so, it calls
	///  `onERC721Received` on `_to` and throws if the return value is not
	///  `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`.
	/// @param _from The current owner of the NFT
	/// @param _to The new owner
	/// @param _tokenId The NFT to transfer
	/// @param _data Additional data with no specified format, sent in call to `_to`
	function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata _data) external /*payable*/;

	/// @notice Transfers the ownership of an NFT from one address to another address
	/// @dev This works identically to the other function with an extra data parameter,
	///  except this function just sets data to "".
	/// @param _from The current owner of the NFT
	/// @param _to The new owner
	/// @param _tokenId The NFT to transfer
	function safeTransferFrom(address _from, address _to, uint256 _tokenId) external /*payable*/;

	/// @notice Transfer ownership of an NFT -- THE CALLER IS RESPONSIBLE
	///  TO CONFIRM THAT `_to` IS CAPABLE OF RECEIVING NFTS OR ELSE
	///  THEY MAY BE PERMANENTLY LOST
	/// @dev Throws unless `msg.sender` is the current owner, an authorized
	///  operator, or the approved address for this NFT. Throws if `_from` is
	///  not the current owner. Throws if `_to` is the zero address. Throws if
	///  `_tokenId` is not a valid NFT.
	/// @param _from The current owner of the NFT
	/// @param _to The new owner
	/// @param _tokenId The NFT to transfer
	function transferFrom(address _from, address _to, uint256 _tokenId) external /*payable*/;

	/// @notice Change or reaffirm the approved address for an NFT
	/// @dev The zero address indicates there is no approved address.
	///  Throws unless `msg.sender` is the current NFT owner, or an authorized
	///  operator of the current owner.
	/// @param _approved The new approved NFT controller
	/// @param _tokenId The NFT to approve
	function approve(address _approved, uint256 _tokenId) external /*payable*/;

	/// @notice Enable or disable approval for a third party ("operator") to manage
	///  all of `msg.sender`'s assets
	/// @dev Emits the ApprovalForAll event. The contract MUST allow
	///  multiple operators per owner.
	/// @param _operator Address to add to the set of authorized operators
	/// @param _approved True if the operator is approved, false to revoke approval
	function setApprovalForAll(address _operator, bool _approved) external;

	/// @notice Get the approved address for a single NFT
	/// @dev Throws if `_tokenId` is not a valid NFT.
	/// @param _tokenId The NFT to find the approved address for
	/// @return The approved address for this NFT, or the zero address if there is none
	function getApproved(uint256 _tokenId) external view returns (address);

	/// @notice Query if an address is an authorized operator for another address
	/// @param _owner The address that owns the NFTs
	/// @param _operator The address that acts on behalf of the owner
	/// @return True if `_operator` is an approved operator for `_owner`, false otherwise
	function isApprovedForAll(address _owner, address _operator) external view returns (bool);
}

/// @dev Note: the ERC-165 identifier for this interface is 0x150b7a02.
interface ERC721TokenReceiver {
	/// @notice Handle the receipt of an NFT
	/// @dev The ERC721 smart contract calls this function on the recipient
	///  after a `transfer`. This function MAY throw to revert and reject the
	///  transfer. Return of other than the magic value MUST result in the
	///  transaction being reverted.
	///  Note: the contract address is always the message sender.
	/// @param _operator The address which called `safeTransferFrom` function
	/// @param _from The address which previously owned the token
	/// @param _tokenId The NFT identifier which is being transferred
	/// @param _data Additional data with no specified format
	/// @return `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
	///  unless throwing
	function onERC721Received(address _operator, address _from, uint256 _tokenId, bytes calldata _data) external returns(bytes4);
}

/**
 * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
 *
 * @notice See https://eips.ethereum.org/EIPS/eip-721
 *
 * @dev The ERC-165 identifier for this interface is 0x5b5e139f.
 *
 * @author William Entriken, Dieter Shirley, Jacob Evans, Nastassia Sachs
 */
interface ERC721Metadata is ERC721 {
	/// @notice A descriptive name for a collection of NFTs in this contract
	function name() external view returns (string memory _name);

	/// @notice An abbreviated name for NFTs in this contract
	function symbol() external view returns (string memory _symbol);

	/// @notice A distinct Uniform Resource Identifier (URI) for a given asset.
	/// @dev Throws if `_tokenId` is not a valid NFT. URIs are defined in RFC
	///  3986. The URI may point to a JSON file that conforms to the "ERC721
	///  Metadata JSON Schema".
	function tokenURI(uint256 _tokenId) external view returns (string memory);
}

/**
 * @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
 *
 * @notice See https://eips.ethereum.org/EIPS/eip-721
 *
 * @dev The ERC-165 identifier for this interface is 0x780e9d63.
 *
 * @author William Entriken, Dieter Shirley, Jacob Evans, Nastassia Sachs
 */
interface ERC721Enumerable is ERC721 {
	/// @notice Count NFTs tracked by this contract
	/// @return A count of valid NFTs tracked by this contract, where each one of
	///  them has an assigned and queryable owner not equal to the zero address
	function totalSupply() external view returns (uint256);

	/// @notice Enumerate valid NFTs
	/// @dev Throws if `_index` >= `totalSupply()`.
	/// @param _index A counter less than `totalSupply()`
	/// @return The token identifier for the `_index`th NFT,
	///  (sort order not specified)
	function tokenByIndex(uint256 _index) external view returns (uint256);

	/// @notice Enumerate NFTs assigned to an owner
	/// @dev Throws if `_index` >= `balanceOf(_owner)` or if
	///  `_owner` is the zero address, representing invalid NFTs.
	/// @param _owner An address where we are interested in NFTs owned by them
	/// @param _index A counter less than `balanceOf(_owner)`
	/// @return The token identifier for the `_index`th NFT assigned to `_owner`,
	///   (sort order not specified)
	function tokenOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256);
}

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

/**
 * @title Mintable ERC721
 *
 * @notice Defines mint capabilities for Alethea ERC721 tokens.
 *      This interface should be treated as a definition of what mintable means for ERC721
 *
 * @author Basil Gorin
 */
interface MintableERC721 {
	/**
	 * @notice Checks if specified token exists
	 *
	 * @dev Returns whether the specified token ID has an ownership
	 *      information associated with it
	 *
	 * @param _tokenId ID of the token to query existence for
	 * @return whether the token exists (true - exists, false - doesn't exist)
	 */
	function exists(uint256 _tokenId) external view returns(bool);

	/**
	 * @dev Creates new token with token ID specified
	 *      and assigns an ownership `_to` for this token
	 *
	 * @dev Unsafe: doesn't execute `onERC721Received` on the receiver.
	 *      Prefer the use of `safeMint` instead of `mint`.
	 *
	 * @dev Should have a restricted access handled by the implementation
	 *
	 * @param _to an address to mint token to
	 * @param _tokenId ID of the token to mint
	 */
	function mint(address _to, uint256 _tokenId) external;

	/**
	 * @dev Creates new tokens starting with token ID specified
	 *      and assigns an ownership `_to` for these tokens
	 *
	 * @dev Token IDs to be minted: [_tokenId, _tokenId + n)
	 *
	 * @dev n must be greater or equal 2: `n > 1`
	 *
	 * @dev Unsafe: doesn't execute `onERC721Received` on the receiver.
	 *      Prefer the use of `safeMintBatch` instead of `mintBatch`.
	 *
	 * @dev Should have a restricted access handled by the implementation
	 *
	 * @param _to an address to mint tokens to
	 * @param _tokenId ID of the first token to mint
	 * @param n how many tokens to mint, sequentially increasing the _tokenId
	 */
	function mintBatch(address _to, uint256 _tokenId, uint256 n) external;

	/**
	 * @dev Creates new token with token ID specified
	 *      and assigns an ownership `_to` for this token
	 *
	 * @dev Checks if `_to` is a smart contract (code size > 0). If so, it calls
	 *      `onERC721Received` on `_to` and throws if the return value is not
	 *      `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`.
	 *
	 * @dev Should have a restricted access handled by the implementation
	 *
	 * @param _to an address to mint token to
	 * @param _tokenId ID of the token to mint
	 */
	function safeMint(address _to, uint256 _tokenId) external;

	/**
	 * @dev Creates new token with token ID specified
	 *      and assigns an ownership `_to` for this token
	 *
	 * @dev Checks if `_to` is a smart contract (code size > 0). If so, it calls
	 *      `onERC721Received` on `_to` and throws if the return value is not
	 *      `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`.
	 *
	 * @dev Should have a restricted access handled by the implementation
	 *
	 * @param _to an address to mint token to
	 * @param _tokenId ID of the token to mint
	 * @param _data additional data with no specified format, sent in call to `_to`
	 */
	function safeMint(address _to, uint256 _tokenId, bytes memory _data) external;

	/**
	 * @dev Creates new tokens starting with token ID specified
	 *      and assigns an ownership `_to` for these tokens
	 *
	 * @dev Token IDs to be minted: [_tokenId, _tokenId + n)
	 *
	 * @dev n must be greater or equal 2: `n > 1`
	 *
	 * @dev Checks if `_to` is a smart contract (code size > 0). If so, it calls
	 *      `onERC721Received` on `_to` and throws if the return value is not
	 *      `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`.
	 *
	 * @dev Should have a restricted access handled by the implementation
	 *
	 * @param _to an address to mint token to
	 * @param _tokenId ID of the token to mint
	 * @param n how many tokens to mint, sequentially increasing the _tokenId
	 */
	function safeMintBatch(address _to, uint256 _tokenId, uint256 n) external;

	/**
	 * @dev Creates new tokens starting with token ID specified
	 *      and assigns an ownership `_to` for these tokens
	 *
	 * @dev Token IDs to be minted: [_tokenId, _tokenId + n)
	 *
	 * @dev n must be greater or equal 2: `n > 1`
	 *
	 * @dev Checks if `_to` is a smart contract (code size > 0). If so, it calls
	 *      `onERC721Received` on `_to` and throws if the return value is not
	 *      `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`.
	 *
	 * @dev Should have a restricted access handled by the implementation
	 *
	 * @param _to an address to mint token to
	 * @param _tokenId ID of the token to mint
	 * @param n how many tokens to mint, sequentially increasing the _tokenId
	 * @param _data additional data with no specified format, sent in call to `_to`
	 */
	function safeMintBatch(address _to, uint256 _tokenId, uint256 n, bytes memory _data) external;
}

/**
 * @title Alethea Burnable ERC721
 *
 * @notice Defines burn capabilities for Alethea ERC721 tokens.
 *      This interface should be treated as a definition of what burnable means for ERC721
 *
 * @author Basil Gorin
 */
interface BurnableERC721 {
	/**
	 * @notice Destroys the token with token ID specified
	 *
	 * @dev Should be accessible publicly by token owners.
	 *      May have a restricted access handled by the implementation
	 *
	 * @param _tokenId ID of the token to burn
	 */
	function burn(uint256 _tokenId) external;
}

/**
 * @title With Base URI
 *
 * @notice A marker interface for the contracts having the baseURI() function
 *      or public string variable named baseURI
 *      NFT implementations like TinyERC721, or ShortERC721 are example of such smart contracts
 *
 * @author Basil Gorin
 */
interface WithBaseURI {
	/**
	 * @dev Usually used in NFT implementations to construct ERC721Metadata.tokenURI as
	 *      `base URI + token ID` if token URI is not set (not present in `_tokenURIs` mapping)
	 *
	 * @dev For example, if base URI is https://api.com/token/, then token #1
	 *      will have an URI https://api.com/token/1
	 */
	function baseURI() external view returns(string memory);
}

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

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";

/**
 * @title Initializable Role-based Access Control (RBAC) // ERC1967Proxy
 *
 * @notice Access control smart contract provides an API to check
 *      if a specific operation is permitted globally and/or
 *      if a particular user has a permission to execute it.
 *
 * @notice This contract is inherited by other contracts requiring the role-based access control (RBAC)
 *      protection for the restricted access functions
 *
 * @notice It deals with two main entities: features and roles.
 *
 * @notice Features are designed to be used to enable/disable public functions
 *      of the smart contract (used by a wide audience).
 * @notice User roles are designed to control the access to restricted functions
 *      of the smart contract (used by a limited set of maintainers).
 *
 * @notice Terms "role", "permissions" and "set of permissions" have equal meaning
 *      in the documentation text and may be used interchangeably.
 * @notice Terms "permission", "single permission" implies only one permission bit set.
 *
 * @notice Access manager is a special role which allows to grant/revoke other roles.
 *      Access managers can only grant/revoke permissions which they have themselves.
 *      As an example, access manager with no other roles set can only grant/revoke its own
 *      access manager permission and nothing else.
 *
 * @notice Access manager permission should be treated carefully, as a super admin permission:
 *      Access manager with even no other permission can interfere with another account by
 *      granting own access manager permission to it and effectively creating more powerful
 *      permission set than its own.
 *
 * @dev Both current and OpenZeppelin AccessControl implementations feature a similar API
 *      to check/know "who is allowed to do this thing".
 * @dev Zeppelin implementation is more flexible:
 *      - it allows setting unlimited number of roles, while current is limited to 256 different roles
 *      - it allows setting an admin for each role, while current allows having only one global admin
 * @dev Current implementation is more lightweight:
 *      - it uses only 1 bit per role, while Zeppelin uses 256 bits
 *      - it allows setting up to 256 roles at once, in a single transaction, while Zeppelin allows
 *        setting only one role in a single transaction
 *
 * @dev This smart contract is designed to be inherited by other
 *      smart contracts which require access control management capabilities.
 *
 * @dev Access manager permission has a bit 255 set.
 *      This bit must not be used by inheriting contracts for any other permissions/features.
 *
 * @dev This is an initializable version of the RBAC, based on Zeppelin implementation,
 *      it can be used for ERC1967 proxies, as well as for EIP-1167 minimal proxies
 *      see https://docs.openzeppelin.com/contracts/4.x/upgradeable
 *      see https://docs.openzeppelin.com/contracts/4.x/api/proxy#UUPSUpgradeable
 *      see https://forum.openzeppelin.com/t/uups-proxies-tutorial-solidity-javascript/7786
 *      see https://eips.ethereum.org/EIPS/eip-1167
 *      see https://docs.openzeppelin.com/contracts/4.x/api/proxy#Clones
 *
 * @author Basil Gorin
 */
abstract contract InitializableAccessControl is Initializable {
	/**
	 * @dev Privileged addresses with defined roles/permissions
	 * @dev In the context of ERC20/ERC721 tokens these can be permissions to
	 *      allow minting or burning tokens, transferring on behalf and so on
	 *
	 * @dev Maps user address to the permissions bitmask (role), where each bit
	 *      represents a permission
	 * @dev Bitmask 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
	 *      represents all possible permissions
	 * @dev 'This' address mapping represents global features of the smart contract
	 *
	 * @dev We keep the mapping private to prevent direct writes to it from the inheriting
	 *      contracts, `getRole()` and `updateRole()` functions should be used instead
	 */
	mapping(address => uint256) private userRoles;

	/**
	 * @dev Empty reserved space in storage. The size of the __gap array is calculated so that
	 *      the amount of storage used by a contract always adds up to the 50.
	 *      See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
	 */
	uint256[49] private __gap;

	/**
	 * @notice Access manager is responsible for assigning the roles to users,
	 *      enabling/disabling global features of the smart contract
	 * @notice Access manager can add, remove and update user roles,
	 *      remove and update global features
	 *
	 * @dev Role ROLE_ACCESS_MANAGER allows modifying user roles and global features
	 * @dev Role ROLE_ACCESS_MANAGER has single bit at position 255 enabled
	 */
	uint256 public constant ROLE_ACCESS_MANAGER = 0x8000000000000000000000000000000000000000000000000000000000000000;

	/**
	 * @notice Upgrade manager is responsible for smart contract upgrades,
	 *      see https://docs.openzeppelin.com/contracts/4.x/api/proxy#UUPSUpgradeable
	 *      see https://docs.openzeppelin.com/contracts/4.x/upgradeable
	 *
	 * @dev Role ROLE_UPGRADE_MANAGER allows passing the _authorizeUpgrade() check
	 * @dev Role ROLE_UPGRADE_MANAGER has single bit at position 254 enabled
	 */
	uint256 public constant ROLE_UPGRADE_MANAGER = 0x4000000000000000000000000000000000000000000000000000000000000000;

	/**
	 * @dev Bitmask representing all the possible permissions (super admin role)
	 * @dev Has all the bits are enabled (2^256 - 1 value)
	 */
	uint256 private constant FULL_PRIVILEGES_MASK = type(uint256).max; // before 0.8.0: uint256(-1) overflows to 0xFFFF...

	/**
	 * @dev Fired in updateRole() and updateFeatures()
	 *
	 * @param operator address which was granted/revoked permissions
	 * @param requested permissions requested
	 * @param assigned permissions effectively set
	 */
	event RoleUpdated(address indexed operator, uint256 requested, uint256 assigned);

	/**
	 * @notice Function modifier making a function defined as public behave as restricted
	 *      (so that only a pre-configured set of accounts can execute it)
	 *
	 * @param role the role transaction executor is required to have;
	 *      the function throws an "access denied" exception if this condition is not met
	 */
	modifier restrictedTo(uint256 role) {
		// verify the access permission
		require(isSenderInRole(role), "access denied");

		// execute the rest of the function
		_;
	}

	/**
	 * @dev Creates/deploys the ACL implementation to be used in a proxy
	 *
	 * @dev Note:
	 *      the implementation is already initialized and
	 *      `_postConstruct` is not executable on the implementation
	 *      `_postConstruct` is still available in the context of a proxy
	 *      and should be executed on the proxy deployment (in the same tx)
	 */
	 // constructor() initializer {}

	/**
	 * @dev Contract initializer, sets the contract owner to have full privileges
	 *
	 * @dev Can be executed only once, reverts when executed second time
	 *
	 * @dev IMPORTANT:
	 *      this function SHOULD be executed during proxy deployment (in the same transaction)
	 *
	 * @param _owner smart contract owner having full privileges
	 */
	function _postConstruct(address _owner) internal virtual onlyInitializing {
		// grant owner full privileges
		__setRole(_owner, FULL_PRIVILEGES_MASK, FULL_PRIVILEGES_MASK);
	}

	/**
	 * @dev Highest version that has been initialized.
	 *      Non-zero value means contract was already initialized.
	 * @dev see {Initializable}, {reinitializer}.
	 *
	 * @return highest version that has been initialized
	 */
/*
	function getInitializedVersion() public view returns(uint64) {
		// delegate to `_getInitializedVersion`
		return _getInitializedVersion();
	}
*/

	/**
	 * @notice Retrieves globally set of features enabled
	 *
	 * @dev Effectively reads userRoles role for the contract itself
	 *
	 * @return 256-bit bitmask of the features enabled
	 */
	function features() public view returns (uint256) {
		// features are stored in 'this' address mapping of `userRoles`
		return getRole(address(this));
	}

	/**
	 * @notice Updates set of the globally enabled features (`features`),
	 *      taking into account sender's permissions
	 *
	 * @dev Requires transaction sender to have `ROLE_ACCESS_MANAGER` permission
	 * @dev Function is left for backward compatibility with older versions
	 *
	 * @param _mask bitmask representing a set of features to enable/disable
	 */
	function updateFeatures(uint256 _mask) public {
		// delegate call to `updateRole`
		updateRole(address(this), _mask);
	}

	/**
	 * @notice Reads the permissions (role) for a given user from the `userRoles` mapping
	 *      (privileged addresses with defined roles/permissions)
	 * @notice In the context of ERC20/ERC721 tokens these can be permissions to
	 *      allow minting or burning tokens, transferring on behalf and so on
	 *
	 * @dev Having a simple getter instead of making the mapping public
	 *      allows enforcing the encapsulation of the mapping and protects from
	 *      writing to it directly in the inheriting smart contracts
	 *
	 * @param operator address of a user to read permissions for,
	 *      or self address to read global features of the smart contract
	 */
	function getRole(address operator) public view returns(uint256) {
		// read the value from `userRoles` and return
		return userRoles[operator];
	}

	/**
	 * @notice Updates set of permissions (role) for a given user,
	 *      taking into account sender's permissions.
	 *
	 * @dev Setting role to zero is equivalent to removing an all permissions
	 * @dev Setting role to `FULL_PRIVILEGES_MASK` is equivalent to
	 *      copying senders' permissions (role) to the user
	 * @dev Requires transaction sender to have `ROLE_ACCESS_MANAGER` permission
	 *
	 * @param operator address of a user to alter permissions for,
	 *       or self address to alter global features of the smart contract
	 * @param role bitmask representing a set of permissions to
	 *      enable/disable for a user specified
	 */
	function updateRole(address operator, uint256 role) public {
		// caller must have a permission to update user roles
		require(isSenderInRole(ROLE_ACCESS_MANAGER), "access denied");

		// evaluate the role and reassign it
		__setRole(operator, role, _evaluateBy(msg.sender, getRole(operator), role));
	}

	/**
	 * @notice Determines the permission bitmask an operator can set on the
	 *      target permission set
	 * @notice Used to calculate the permission bitmask to be set when requested
	 *     in `updateRole` and `updateFeatures` functions
	 *
	 * @dev Calculated based on:
	 *      1) operator's own permission set read from userRoles[operator]
	 *      2) target permission set - what is already set on the target
	 *      3) desired permission set - what do we want set target to
	 *
	 * @dev Corner cases:
	 *      1) Operator is super admin and its permission set is `FULL_PRIVILEGES_MASK`:
	 *        `desired` bitset is returned regardless of the `target` permission set value
	 *        (what operator sets is what they get)
	 *      2) Operator with no permissions (zero bitset):
	 *        `target` bitset is returned regardless of the `desired` value
	 *        (operator has no authority and cannot modify anything)
	 *
	 * @dev Example:
	 *      Consider an operator with the permissions bitmask     00001111
	 *      is about to modify the target permission set          01010101
	 *      Operator wants to set that permission set to          00110011
	 *      Based on their role, an operator has the permissions
	 *      to update only lowest 4 bits on the target, meaning that
	 *      high 4 bits of the target set in this example is left
	 *      unchanged and low 4 bits get changed as desired:      01010011
	 *
	 * @param operator address of the contract operator which is about to set the permissions
	 * @param target input set of permissions to operator is going to modify
	 * @param desired desired set of permissions operator would like to set
	 * @return resulting set of permissions given operator will set
	 */
	function _evaluateBy(address operator, uint256 target, uint256 desired) internal view returns (uint256) {
		// read operator's permissions
		uint256 p = getRole(operator);

		// taking into account operator's permissions,
		// 1) enable the permissions desired on the `target`
		target |= p & desired;
		// 2) disable the permissions desired on the `target`
		target &= FULL_PRIVILEGES_MASK ^ (p & (FULL_PRIVILEGES_MASK ^ desired));

		// return calculated result
		return target;
	}

	/**
	 * @notice Checks if requested set of features is enabled globally on the contract
	 *
	 * @param required set of features to check against
	 * @return true if all the features requested are enabled, false otherwise
	 */
	function isFeatureEnabled(uint256 required) public view returns (bool) {
		// delegate call to `__hasRole`, passing `features` property
		return __hasRole(features(), required);
	}

	/**
	 * @notice Checks if transaction sender `msg.sender` has all the permissions required
	 *
	 * @dev Used in smart contracts only. Off-chain clients should use `isOperatorInRole`.
	 *
	 * @param required set of permissions (role) to check against
	 * @return true if all the permissions requested are enabled, false otherwise
	 */
	function isSenderInRole(uint256 required) public view returns (bool) {
		// delegate call to `isOperatorInRole`, passing transaction sender
		return isOperatorInRole(msg.sender, required);
	}

	/**
	 * @notice Checks if operator has all the permissions (role) required
	 *
	 * @param operator address of the user to check role for
	 * @param required set of permissions (role) to check
	 * @return true if all the permissions requested are enabled, false otherwise
	 */
	function isOperatorInRole(address operator, uint256 required) public view returns (bool) {
		// delegate call to `__hasRole`, passing operator's permissions (role)
		return __hasRole(getRole(operator), required);
	}

	/**
	 * @dev Sets the `assignedRole` role to the operator, logs both `requestedRole` and `actualRole`
	 *
	 * @dev Unsafe:
	 *      provides direct write access to `userRoles` mapping without any security checks,
	 *      doesn't verify the executor (msg.sender) permissions,
	 *      must be kept private at all times
	 *
	 * @param operator address of a user to alter permissions for,
	 *       or self address to alter global features of the smart contract
	 * @param requestedRole bitmask representing a set of permissions requested
	 *      to be enabled/disabled for a user specified, used only to be logged into event
	 * @param assignedRole bitmask representing a set of permissions to
	 *      enable/disable for a user specified, used to update the mapping and to be logged into event
	 */
	function __setRole(address operator, uint256 requestedRole, uint256 assignedRole) private {
		// assign the role to the operator
		userRoles[operator] = assignedRole;

		// fire an event
		emit RoleUpdated(operator, requestedRole, assignedRole);
	}

	/**
	 * @dev Checks if role `actual` contains all the permissions required `required`
	 *
	 * @param actual existent role
	 * @param required required role
	 * @return true if actual has required role (all permissions), false otherwise
	 */
	function __hasRole(uint256 actual, uint256 required) private pure returns (bool) {
		// check the bitmask for the role required and return the result
		return actual & required == required;
	}
}

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

/**
 * @notice Replaces built-in Solidity address.transfer and address.send functions
 *      with the address.call function
 */
library Transfers {
	/// @dev Mimics address.send forwarding 4,900 gas
	function send(address payable to, uint256 value) internal returns(bool) {
		(bool success, ) = to.call{gas: 4900, value: value}("");
		return success;
	}

	/// @dev Mimics address.transfer forwarding 4,900 gas
	function transfer(address payable to, uint256 value) internal {
		require(send(to, value), "failed to send ether");
	}

	/// @dev Alias for `send`
	function send1(address payable to, uint256 value) internal returns(bool) {
		return send(to, value);
	}

	/// @dev Alias for `transfer`
	function transfer1(address payable to, uint256 value) internal {
		transfer(to, value);
	}
}

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

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"components":[{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"internalType":"struct TradeableShares.SharesSubject","name":"_sharesSubject","type":"tuple"},{"internalType":"address","name":"_protocolFeeDestination","type":"address"},{"internalType":"uint64","name":"_protocolFeePercent","type":"uint64"},{"internalType":"contract HoldersRewardsDistributor","name":"_holdersFeeDestination","type":"address"},{"internalType":"uint64","name":"_holdersFeePercent","type":"uint64"},{"internalType":"uint64","name":"_subjectFeePercent","type":"uint64"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"address","name":"_beneficiary","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldProtocolFeePercent","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newProtocolFeePercent","type":"uint256"}],"name":"HoldersFeeDisabled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldProtocolFeeDestination","type":"address"},{"indexed":false,"internalType":"address","name":"newProtocolFeeDestination","type":"address"}],"name":"ProtocolFeeDestinationUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"uint256","name":"requested","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"assigned","type":"uint256"}],"name":"RoleUpdated","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"indexed":false,"internalType":"struct TradeableShares.SharesSubject","name":"oldSubject","type":"tuple"},{"components":[{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"indexed":false,"internalType":"struct TradeableShares.SharesSubject","name":"newSubject","type":"tuple"},{"indexed":false,"internalType":"contract SharesFactory","name":"factory","type":"address"}],"name":"SharesSubjectUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"beneficiary","type":"address"},{"indexed":true,"internalType":"address","name":"issuer","type":"address"},{"indexed":true,"internalType":"bool","name":"isBuy","type":"bool"},{"indexed":false,"internalType":"uint256","name":"sharesAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"paidAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"protocolFeeAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"holdersFeeAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"subjectFeeAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"supply","type":"uint256"}],"name":"Trade","type":"event"},{"inputs":[],"name":"ROLE_ACCESS_MANAGER","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ROLE_HOLDERS_FEE_MANAGER","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ROLE_PROTOCOL_FEE_MANAGER","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ROLE_SHARES_SUBJECT_MANAGER","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ROLE_UPGRADE_MANAGER","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"buyKeys","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"beneficiary","type":"address"}],"name":"buyKeysTo","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"buyShares","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"beneficiary","type":"address"}],"name":"buySharesTo","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"disableHoldersFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"features","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"getBuyPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_supply","type":"uint256"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"getBuyPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"_supply","type":"uint256"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"uint256","name":"_protocolFeePercent","type":"uint256"},{"internalType":"uint256","name":"_holdersFeePercent","type":"uint256"},{"internalType":"uint256","name":"_subjectFeePercent","type":"uint256"}],"name":"getBuyPriceAfterFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"getBuyPriceAfterFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getHoldersFeeDestination","outputs":[{"internalType":"contract HoldersRewardsDistributor","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getHoldersFeeInfo","outputs":[{"internalType":"contract HoldersRewardsDistributor","name":"feeDestination","type":"address"},{"internalType":"uint256","name":"feePercent","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getHoldersFeePercent","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"supply","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"getPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getProtocolFeeDestination","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getProtocolFeeInfo","outputs":[{"internalType":"address","name":"feeDestination","type":"address"},{"internalType":"uint256","name":"feePercent","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getProtocolFeePercent","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"getRole","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_supply","type":"uint256"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"getSellPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"getSellPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"getSellPriceAfterFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_supply","type":"uint256"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"uint256","name":"_protocolFeePercent","type":"uint256"},{"internalType":"uint256","name":"_holdersFeePercent","type":"uint256"},{"internalType":"uint256","name":"_subjectFeePercent","type":"uint256"}],"name":"getSellPriceAfterFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"_holder","type":"address"}],"name":"getSharesBalance","outputs":[{"internalType":"uint256","name":"balance","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getSharesIssuer","outputs":[{"internalType":"address","name":"nftOwner","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getSharesSubject","outputs":[{"components":[{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"internalType":"struct TradeableShares.SharesSubject","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getSharesSupply","outputs":[{"internalType":"uint256","name":"supply","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getSubjectFeeInfo","outputs":[{"internalType":"address","name":"feeDestination","type":"address"},{"internalType":"uint256","name":"feePercent","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getSubjectFeePercent","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTradeVolume","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"required","type":"uint256"}],"name":"isFeatureEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"uint256","name":"required","type":"uint256"}],"name":"isOperatorInRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"required","type":"uint256"}],"name":"isSenderInRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"components":[{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"internalType":"struct TradeableShares.SharesSubject","name":"_sharesSubject","type":"tuple"},{"internalType":"address","name":"_protocolFeeDestination","type":"address"},{"internalType":"uint64","name":"_protocolFeePercent","type":"uint64"},{"internalType":"contract HoldersRewardsDistributor","name":"_holdersFeeDestination","type":"address"},{"internalType":"uint64","name":"_holdersFeePercent","type":"uint64"},{"internalType":"uint64","name":"_subjectFeePercent","type":"uint64"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"address","name":"_beneficiary","type":"address"}],"name":"postConstruct","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"sellKeys","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address payable","name":"beneficiary","type":"address"}],"name":"sellKeysTo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"sellShares","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address payable","name":"beneficiary","type":"address"}],"name":"sellSharesTo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_mask","type":"uint256"}],"name":"updateFeatures","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_protocolFeeDestination","type":"address"}],"name":"updateProtocolFeeDestination","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"uint256","name":"role","type":"uint256"}],"name":"updateRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"internalType":"struct TradeableShares.SharesSubject","name":"_sharesSubject","type":"tuple"}],"name":"updateSharesSubject","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"internalType":"struct TradeableShares.SharesSubject","name":"_sharesSubject","type":"tuple"},{"internalType":"contract SharesFactory","name":"_factory","type":"address"}],"name":"updateSharesSubject","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60806040523480156200001157600080fd5b5060405162002fb738038062002fb7833981016040819052620000349162000d4b565b888888888888888888600060019054906101000a900460ff166200005f5760005460ff161562000069565b6200006962000123565b620000c15760405162461bcd60e51b815260206004820152602e602482015260008051602062002f7783398151915260448201526d191e481a5b9a5d1a585b1a5e995960921b60648201526084015b60405180910390fd5b600054610100900460ff16158015620000e4576000805461ffff19166101011790555b620000f78a8a8a8a8a8a8a8a8a62000141565b80156200010a576000805461ff00191690555b5050505050505050505050505050505050505062000f69565b60006200013b306200025260201b620012d51760201c565b15905090565b600054610100900460ff166200015e5760005460ff161562000168565b6200016862000123565b620001bc5760405162461bcd60e51b815260206004820152602e602482015260008051602062002f7783398151915260448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608401620000b8565b600054610100900460ff16158015620001df576000805461ffff19166101011790555b620001f08a8a8a8a8a8a8a62000258565b8215620002095762000203838362000348565b62000233565b341562000233576200023334336001600160a01b03166200052560201b620012db1790919060201c565b801562000246576000805461ff00191690555b50505050505050505050565b3b151590565b600054610100900460ff16620002b45760405162461bcd60e51b815260206004820152602b602482015260008051602062002f9783398151915260448201526a6e697469616c697a696e6760a81b6064820152608401620000b8565b620002bf8762000535565b8551603380546001600160a01b0319166001600160a01b03928316179055602090960151603455603580549587166001600160e01b031996871617600160a01b6001600160401b039687168102919091179091556036805494909716939095169290921790831690930292909217909255603780546001600160401b0319169190921617905550565b60006200035460385490565b6001600160a01b0383166000908152603960205260408120805492935085929091906200038390849062000e6d565b90915550620003959050838262000e6d565b6038556000620003a68285620005a3565b90506000620003b582620005d1565b925050506000620003d083600188886200064860201b60201c565b92505050600080620003e8856200078660201b60201c565b9250509150600081848688620003ff919062000e6d565b6200040b919062000e6d565b62000417919062000e6d565b9050803410156200046b5760405162461bcd60e51b815260206004820152601b60248201527f696e73756666696369656e742076616c756520737570706c69656400000000006044820152606401620000b8565b80341115620004a257620004a262000484823462000e88565b336001600160a01b03166200052560201b620012db1790919060201c565b620004ad8662000794565b603854604080518b815260208101899052908101879052606081018690526080810184905260a08101919091526001906001600160a01b0380861691908b16907f952ff8d90add9fdeaeb478102d54441cf0cc0cbe53b1d99e51f747cdc8379e549060c00160405180910390a4505050505050505050565b620005318282620007b0565b5050565b600054610100900460ff16620005915760405162461bcd60e51b815260206004820152602b602482015260008051602062002f9783398151915260448201526a6e697469616c697a696e6760a81b6064820152608401620000b8565b620005a081600019806200080a565b50565b60006002620005be84846200086460201b620012e51760201c565b620005ca919062000ea2565b9392505050565b60008080620005df620009b3565b9093509150670de0b6b3a7640000620005f9838662000ec5565b62000605919062000ea2565b905080158015906200063757506200063581846001600160a01b031662000a0460201b620014071790919060201c565b155b1562000641575060005b9193909250565b600080806200065662000a12565b9093509150670de0b6b3a764000062000670838962000ec5565b6200067c919062000ea2565b90506001600160a01b038316158015906200069657508415155b156200077c57604080516001600160a01b0386166020820152871515918101919091526060810186905260009060800160405160208183030381529060405290506000846001600160a01b03168383604051620006f4919062000ee7565b60006040518083038185875af1925050503d806000811462000733576040519150601f19603f3d011682016040523d82523d6000602084013e62000738565b606091505b5050905080620007795760405162461bcd60e51b815260206004820152600b60248201526a1cde5b98c819985a5b195960aa1b6044820152606401620000b8565b50505b9450945094915050565b60008080620005df62000a54565b80603a6000828254620007a8919062000e6d565b909155505050565b620007bc828262000a85565b620005315760405162461bcd60e51b815260206004820152601460248201527f6661696c656420746f2073656e642065746865720000000000000000000000006044820152606401620000b8565b6001600160a01b03831660008181526001602090815260409182902084905581518581529081018490527fe9be537308880e0f56b7d7cfd7abf85f14c4934486d138f848b92a0cbaf659b4910160405180910390a2505050565b6000808315620008cf5760066200087d60018662000e88565b6200088a90600262000ec5565b6200089790600162000e6d565b85620008a560018262000e88565b620008b1919062000ec5565b620008bd919062000ec5565b620008c9919062000ea2565b620008d2565b60005b9050600084158015620008e6575060018411155b620009715760066001620008fb868862000e6d565b62000907919062000e88565b6200091490600262000ec5565b6200092190600162000e6d565b6200092d868862000e6d565b60016200093b888a62000e6d565b62000947919062000e88565b62000953919062000ec5565b6200095f919062000ec5565b6200096b919062000ea2565b62000974565b60005b9050600062000984838362000e88565b9050613e806200099d82670de0b6b3a764000062000ec5565b620009a9919062000ea2565b9695505050505050565b600080620009c96035546001600160a01b031690565b91506001600160a01b03821615620009fb57620009f5603554600160a01b90046001600160401b031690565b620009fe565b60005b90509091565b6000620005ca838362000a85565b60008062000a286036546001600160a01b031690565b91506001600160a01b03821615620009fb57620009f5603654600160a01b90046001600160401b031690565b60008062000a6162000ae9565b91506001600160a01b03821615620009fb576037546001600160401b0316620009fe565b600080836001600160a01b031661132484604051600060405180830381858888f193505050503d806000811462000ad9576040519150601f19603f3d011682016040523d82523d6000602084013e62000ade565b606091505b509095945050505050565b604080518082019091526033546001600160a01b0316815260345460208083019190915260009162000b24916200141362000b29821b17901c565b905090565b80516000903b1562000d035760008083600001516001600160a01b0316611324634f558e7960e01b866020015160405160240162000b6991815260200190565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b031990941693909317909252905162000ba9919062000ee7565b6000604051808303818686fa925050503d806000811462000be7576040519150601f19603f3d011682016040523d82523d6000602084013e62000bec565b606091505b5091509150811562000c1d578080602001905181019062000c0e919062000f25565b62000c1d575060009392505050565b505060008083600001516001600160a01b0316611324636352211e60e01b866020015160405160240162000c5391815260200190565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b031990941693909317909252905162000c93919062000ee7565b6000604051808303818686fa925050503d806000811462000cd1576040519150601f19603f3d011682016040523d82523d6000602084013e62000cd6565b606091505b5091509150811562000d00578080602001905181019062000cf8919062000f49565b949350505050565b50505b506000919050565b6001600160a01b0381168114620005a057600080fd5b805162000d2e8162000d0b565b919050565b80516001600160401b038116811462000d2e57600080fd5b6000806000806000806000806000898b0361014081121562000d6c57600080fd5b8a5162000d798162000d0b565b99506040601f198201121562000d8e57600080fd5b50604080519081016001600160401b038111828210171562000dc057634e487b7160e01b600052604160045260246000fd5b60405260208b015162000dd38162000d0b565b815260408b01516020820152975062000def60608b0162000d21565b965062000dff60808b0162000d33565b955062000e0f60a08b0162000d21565b945062000e1f60c08b0162000d33565b935062000e2f60e08b0162000d33565b92506101008a0151915062000e486101208b0162000d21565b90509295985092959850929598565b634e487b7160e01b600052601160045260246000fd5b6000821982111562000e835762000e8362000e57565b500190565b60008282101562000e9d5762000e9d62000e57565b500390565b60008262000ec057634e487b7160e01b600052601260045260246000fd5b500490565b600081600019048311821515161562000ee25762000ee262000e57565b500290565b6000825160005b8181101562000f0a576020818601810151858301520162000eee565b8181111562000f1a576000828501525b509190910192915050565b60006020828403121562000f3857600080fd5b81518015158114620005ca57600080fd5b60006020828403121562000f5c57600080fd5b8151620005ca8162000d0b565b611ffe8062000f796000396000f3fe6080604052600436106102885760003560e01c80637573af8e1161015a578063ba730e53116100c1578063d1a93d181161007a578063d1a93d18146107df578063d5bb7f67146107f2578063d7c9423014610812578063d992f1ab14610832578063ef0c256914610847578063fcc2c0781461086757600080fd5b8063ba730e5314610732578063be1125b514610752578063c157253d14610767578063c688d69314610787578063cd3f29e9146107a7578063cfdc8b02146107cc57600080fd5b8063adc8d09d11610113578063adc8d09d14610698578063ae5b102e146106b8578063ae60bda4146106d8578063ae682e2e146106f0578063b1c6ffcf14610708578063b32820e91461071f57600080fd5b80637573af8e146105e95780638db462fc146105fe5780639477d85d1461061c578063974236cb1461063c5780639e59e2f214610653578063a58379a11461067857600080fd5b8063427e3618116101fe57806358ab4e90116101b757806358ab4e90146105185780635cf4ee91146105445780635f100c5c14610564578063695639b7146105845780636b4ed02a14610599578063725f3626146105b957600080fd5b8063427e3618146104695780634377053b1461047c578063442767331461048f5780634526bc3f146104c55780634ba8ae81146104da57806353b73799146104fa57600080fd5b806319786af81161025057806319786af81461037657806320357321146103ac578063252a1ccb146103c15780632b521416146103e1578063357d7ec91461040357806337e36f831461043757600080fd5b806305c3cf6a1461028d57806306ae0df9146102af57806308d4db141461030857806308f97dd81461033657806319224cb414610356575b600080fd5b34801561029957600080fd5b506102ad6102a8366004611b38565b610887565b005b3480156102bb57600080fd5b506040805180820182526000808252602091820152815180830183526033546001600160a01b03168082526034549183019182528351908152905191810191909152015b60405180910390f35b34801561031457600080fd5b50610328610323366004611b68565b610895565b6040519081526020016102ff565b34801561034257600080fd5b50610328610351366004611b68565b6108af565b34801561036257600080fd5b50610328610371366004611b81565b6108f5565b34801561038257600080fd5b50610328610391366004611bcc565b6001600160a01b031660009081526039602052604090205490565b3480156103b857600080fd5b50603854610328565b3480156103cd57600080fd5b506102ad6103dc366004611bcc565b610995565b3480156103ed57600080fd5b5030600090815260016020526040902054610328565b34801561040f57600080fd5b50610418610a34565b604080516001600160a01b0390931683526020830191909152016102ff565b34801561044357600080fd5b506036546001600160a01b03165b6040516001600160a01b0390911681526020016102ff565b6102ad610477366004611b38565b610a6a565b6102ad61048a366004611b38565b610af6565b34801561049b57600080fd5b506103286104aa366004611bcc565b6001600160a01b031660009081526001602052604090205490565b3480156104d157600080fd5b506102ad610b00565b3480156104e657600080fd5b506102ad6104f5366004611b68565b610c5a565b34801561050657600080fd5b506037546001600160401b0316610328565b34801561052457600080fd5b5061052f6202000081565b60405163ffffffff90911681526020016102ff565b34801561055057600080fd5b5061032861055f366004611be9565b610c67565b34801561057057600080fd5b506102ad61057f366004611c23565b610c86565b34801561059057600080fd5b50610418610c91565b3480156105a557600080fd5b506103286105b4366004611b68565b610ccf565b3480156105c557600080fd5b506105d96105d4366004611b68565b610d0c565b60405190151581526020016102ff565b3480156105f557600080fd5b50610451610d28565b34801561060a57600080fd5b506035546001600160a01b0316610451565b34801561062857600080fd5b50610328610637366004611be9565b610d54565b34801561064857600080fd5b5061052f6208000081565b34801561065f57600080fd5b50603654600160a01b90046001600160401b0316610328565b34801561068457600080fd5b50610328610693366004611b81565b610d69565b3480156106a457600080fd5b506102ad6106b3366004611b68565b610dfb565b3480156106c457600080fd5b506102ad6106d3366004611c3f565b610e04565b3480156106e457600080fd5b50610328600160fe1b81565b3480156106fc57600080fd5b50610328600160ff1b81565b34801561071457600080fd5b5061052f6201000081565b6102ad61072d366004611b68565b610e89565b34801561073e57600080fd5b5061032861074d366004611b68565b610e92565b34801561075e57600080fd5b50603a54610328565b34801561077357600080fd5b50610328610782366004611be9565b610ea6565b34801561079357600080fd5b506105d96107a2366004611c3f565b610eb2565b3480156107b357600080fd5b50603554600160a01b90046001600160401b0316610328565b6102ad6107da366004611c82565b610ed4565b6102ad6107ed366004611b68565b610fca565b3480156107fe57600080fd5b506102ad61080d366004611b68565b610fd4565b34801561081e57600080fd5b506102ad61082d366004611b38565b610fde565b34801561083e57600080fd5b506104186111b2565b34801561085357600080fd5b506102ad610862366004611d7b565b6111f0565b34801561087357600080fd5b506105d9610882366004611b68565b6112c9565b6108918282610fde565b5050565b60006108a96108a360385490565b83610ea6565b92915050565b6000806108ba610c91565b91505060006108c76111b2565b91505060006108d4610a34565b9150506108ec6108e360385490565b86858585610d69565b95945050505050565b6000806109028787610ea6565b90506000670de0b6b3a76400006109198784611dbe565b6109239190611ddd565b90506000670de0b6b3a764000061093a8785611dbe565b6109449190611ddd565b90506000670de0b6b3a764000061095b8786611dbe565b6109659190611ddd565b905080826109738587611dff565b61097d9190611dff565b6109879190611dff565b9a9950505050505050505050565b6109a1620100006112c9565b6109c65760405162461bcd60e51b81526004016109bd90611e17565b60405180910390fd5b603554604080516001600160a01b03928316815291831660208301527f56193c420686fede45e4df7337054c2c9d6e74ea3d4773ab0d4f18197b08142d910160405180910390a1603580546001600160a01b0319166001600160a01b0392909216919091179055565b905090565b600080610a3f610d28565b91506001600160a01b03821615610a61576037546001600160401b0316610a64565b60005b90509091565b6000610a7560385490565b1180610a90575033610a85610d28565b6001600160a01b0316145b610aec5760405162461bcd60e51b815260206004820152602760248201527f6f6e6c7920746865206973737565722063616e206275792074686520666972736044820152667420736861726560c81b60648201526084016109bd565b61089182826115e3565b6108918282610a6a565b610b0c620200006112c9565b610b285760405162461bcd60e51b81526004016109bd90611e17565b6036546001600160a01b0316151580610b525750603654600160a01b90046001600160401b031615155b610b8c5760405162461bcd60e51b815260206004820152600b60248201526a1b9bdd08195b98589b195960aa1b60448201526064016109bd565b6035546036547fbf3d73ac4d2b03dfb17b5ef50c84465692647f0843b94f9ebc4062034a6f8e0b916001600160401b03600160a01b91829004811692610bd59290041682611e3e565b604080516001600160401b0393841681529290911660208301520160405180910390a1603654603580546001600160401b03600160a01b93849004811693601492610c2592869290910416611e3e565b82546001600160401b039182166101009390930a928302919092021990911617905550603680546001600160e01b0319169055565b610c648133610fde565b50565b60006002610c7584846112e5565b610c7f9190611ddd565b9392505050565b610c648160006111f0565b600080610ca66035546001600160a01b031690565b91506001600160a01b03821615610a6157603554600160a01b90046001600160401b0316610a64565b600080610cda610c91565b9150506000610ce76111b2565b9150506000610cf4610a34565b9150506108ec610d0360385490565b868585856108f5565b306000908152600160205260408120546108a9905b8316831490565b604080518082019091526033546001600160a01b031681526034546020820152600090610a2f90611413565b6000610c7f610d638385611e69565b83610c67565b600080610d768787610d54565b90506000670de0b6b3a7640000610d8d8784611dbe565b610d979190611ddd565b90506000670de0b6b3a7640000610dae8785611dbe565b610db89190611ddd565b90506000670de0b6b3a7640000610dcf8786611dbe565b610dd99190611ddd565b90508082610de78587611e69565b610df19190611e69565b6109879190611e69565b610c6481610c5a565b610e11600160ff1b6112c9565b610e2d5760405162461bcd60e51b81526004016109bd90611e17565b6108918282610e8433610e55876001600160a01b031660009081526001602052604090205490565b6001600160a01b0391909116600090815260016020526040902054600019808818821618908716919091171690565b611774565b610c6481610fca565b60006108a9610ea060385490565b83610d54565b6000610c7f8383610c67565b6001600160a01b038216600090815260016020526040812054610c7f90610d21565b600054610100900460ff16610eef5760005460ff1615610ef3565b303b155b610f565760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b60648201526084016109bd565b600054610100900460ff16158015610f78576000805461ffff19166101011790555b610f878a8a8a8a8a8a8a6117ce565b8215610f9c57610f9783836115e3565b610fac565b3415610fac57610fac33346112db565b8015610fbe576000805461ff00191690555b50505050505050505050565b610c648133610a6a565b610c643082610e04565b6000610fe960385490565b905082811161103a5760405162461bcd60e51b815260206004820152601a60248201527f63616e6e6f742073656c6c20746865206c61737420736861726500000000000060448201526064016109bd565b33600090815260396020526040902054838110156110905760405162461bcd60e51b8152602060048201526013602482015272696e73756666696369656e742073686172657360681b60448201526064016109bd565b61109a8482611e69565b336000908152603960205260409020556110b48483611e69565b60388190556000906110c69086610c67565b905060006110d382611888565b9250505060006110e683600089336118e8565b925050506000806110f685611a19565b92505091506111308184868861110c9190611e69565b6111169190611e69565b6111209190611e69565b6001600160a01b038a16906112db565b61113985611a26565b603854604080518b815260208101889052908101869052606081018590526080810183905260a08101919091526000906001600160a01b0380851691908b16907f952ff8d90add9fdeaeb478102d54441cf0cc0cbe53b1d99e51f747cdc8379e549060c0015b60405180910390a4505050505050505050565b6000806111c76036546001600160a01b031690565b91506001600160a01b03821615610a6157603654600160a01b90046001600160401b0316610a64565b6111fc620800006112c9565b6112185760405162461bcd60e51b81526004016109bd90611e17565b7f4b356af3260ac232dff1ad2c4ce0f2d58640a02df64c6b965db54e527ce40c886033838360405161124c93929190611e80565b60405180910390a18160336112618282611ecd565b50506001600160a01b0381161561089157806001600160a01b0316637b80be6b6040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156112ad57600080fd5b505af11580156112c1573d6000803e3d6000fd5b505050505050565b60006108a93383610eb2565b3b151590565b6108918282611a40565b60008083156113405760066112fb600186611e69565b611306906002611dbe565b611311906001611dff565b8561131d600182611e69565b6113279190611dbe565b6113319190611dbe565b61133b9190611ddd565b611343565b60005b9050600084158015611356575060018411155b6113cb57600660016113688688611dff565b6113729190611e69565b61137d906002611dbe565b611388906001611dff565b6113928688611dff565b600161139e888a611dff565b6113a89190611e69565b6113b29190611dbe565b6113bc9190611dbe565b6113c69190611ddd565b6113ce565b60005b905060006113dc8383611e69565b9050613e806113f382670de0b6b3a7640000611dbe565b6113fd9190611ddd565b9695505050505050565b6000610c7f8383611a8d565b80516000903b156115db5760008083600001516001600160a01b0316611324634f558e7960e01b866020015160405160240161145191815260200190565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b031990941693909317909252905161148f9190611f03565b6000604051808303818686fa925050503d80600081146114cb576040519150601f19603f3d011682016040523d82523d6000602084013e6114d0565b606091505b509150915081156114fd57808060200190518101906114ef9190611f3e565b6114fd575060009392505050565b505060008083600001516001600160a01b0316611324636352211e60e01b866020015160405160240161153291815260200190565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b03199094169390931790925290516115709190611f03565b6000604051808303818686fa925050503d80600081146115ac576040519150601f19603f3d011682016040523d82523d6000602084013e6115b1565b606091505b509150915081156115d857808060200190518101906115d09190611f60565b949350505050565b50505b506000919050565b60006115ee60385490565b6001600160a01b03831660009081526039602052604081208054929350859290919061161b908490611dff565b9091555061162b90508382611dff565b603855600061163a8285610c67565b9050600061164782611888565b92505050600061165a83600188886118e8565b9250505060008061166a85611a19565b925050915060008184868861167f9190611dff565b6116899190611dff565b6116939190611dff565b9050803410156116e55760405162461bcd60e51b815260206004820152601b60248201527f696e73756666696369656e742076616c756520737570706c696564000000000060448201526064016109bd565b80341115611701576117016116fa8234611e69565b33906112db565b61170a86611a26565b603854604080518b815260208101899052908101879052606081018690526080810184905260a08101919091526001906001600160a01b0380861691908b16907f952ff8d90add9fdeaeb478102d54441cf0cc0cbe53b1d99e51f747cdc8379e549060c00161119f565b6001600160a01b03831660008181526001602090815260409182902084905581518581529081018490527fe9be537308880e0f56b7d7cfd7abf85f14c4934486d138f848b92a0cbaf659b4910160405180910390a2505050565b600054610100900460ff166117f55760405162461bcd60e51b81526004016109bd90611f7d565b6117fe87611aef565b8551603380546001600160a01b0319166001600160a01b03928316179055602090960151603455603580549587166001600160e01b031996871617600160a01b6001600160401b0396871681029190911790915560368054949097169390951692909217908316909302929092179092556037805467ffffffffffffffff19169190921617905550565b6000806000611895610c91565b9093509150670de0b6b3a76400006118ad8386611dbe565b6118b79190611ddd565b905080158015906118d857506118d66001600160a01b03841682611407565b155b156118e1575060005b9193909250565b60008060006118f56111b2565b9093509150670de0b6b3a764000061190d8389611dbe565b6119179190611ddd565b90506001600160a01b0383161580159061193057508415155b15611a0f57604080516001600160a01b0386166020820152871515918101919091526060810186905260009060800160405160208183030381529060405290506000846001600160a01b0316838360405161198b9190611f03565b60006040518083038185875af1925050503d80600081146119c8576040519150601f19603f3d011682016040523d82523d6000602084013e6119cd565b606091505b5050905080611a0c5760405162461bcd60e51b815260206004820152600b60248201526a1cde5b98c819985a5b195960aa1b60448201526064016109bd565b50505b9450945094915050565b6000806000611895610a34565b80603a6000828254611a389190611dff565b909155505050565b611a4a8282611a8d565b6108915760405162461bcd60e51b81526020600482015260146024820152733330b4b632b2103a379039b2b7321032ba3432b960611b60448201526064016109bd565b600080836001600160a01b031661132484604051600060405180830381858888f193505050503d8060008114611adf576040519150601f19603f3d011682016040523d82523d6000602084013e611ae4565b606091505b509095945050505050565b600054610100900460ff16611b165760405162461bcd60e51b81526004016109bd90611f7d565b610c648160001980611774565b6001600160a01b0381168114610c6457600080fd5b60008060408385031215611b4b57600080fd5b823591506020830135611b5d81611b23565b809150509250929050565b600060208284031215611b7a57600080fd5b5035919050565b600080600080600060a08688031215611b9957600080fd5b505083359560208501359550604085013594606081013594506080013592509050565b8035611bc781611b23565b919050565b600060208284031215611bde57600080fd5b8135610c7f81611b23565b60008060408385031215611bfc57600080fd5b50508035926020909101359150565b600060408284031215611c1d57600080fd5b50919050565b600060408284031215611c3557600080fd5b610c7f8383611c0b565b60008060408385031215611c5257600080fd5b8235611c5d81611b23565b946020939093013593505050565b80356001600160401b0381168114611bc757600080fd5b6000806000806000806000806000898b03610140811215611ca257600080fd5b8a35611cad81611b23565b99506040601f1982011215611cc157600080fd5b50604051604081018181106001600160401b0382111715611cf257634e487b7160e01b600052604160045260246000fd5b60405260208b0135611d0381611b23565b815260408b013560208201529750611d1d60608b01611bbc565b9650611d2b60808b01611c6b565b9550611d3960a08b01611bbc565b9450611d4760c08b01611c6b565b9350611d5560e08b01611c6b565b92506101008a01359150611d6c6101208b01611bbc565b90509295985092959850929598565b60008060608385031215611d8e57600080fd5b611d988484611c0b565b91506040830135611b5d81611b23565b634e487b7160e01b600052601160045260246000fd5b6000816000190483118215151615611dd857611dd8611da8565b500290565b600082611dfa57634e487b7160e01b600052601260045260246000fd5b500490565b60008219821115611e1257611e12611da8565b500190565b6020808252600d908201526c1858d8d95cdcc819195b9a5959609a1b604082015260600190565b60006001600160401b03808316818516808303821115611e6057611e60611da8565b01949350505050565b600082821015611e7b57611e7b611da8565b500390565b83546001600160a01b0390811682526001850154602083015260a08201908435611ea981611b23565b81811660408501525060208501356060840152808416608084015250949350505050565b8135611ed881611b23565b81546001600160a01b0319166001600160a01b03919091161781556020919091013560019190910155565b6000825160005b81811015611f245760208186018101518583015201611f0a565b81811115611f33576000828501525b509190910192915050565b600060208284031215611f5057600080fd5b81518015158114610c7f57600080fd5b600060208284031215611f7257600080fd5b8151610c7f81611b23565b6020808252602b908201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960408201526a6e697469616c697a696e6760a81b60608201526080019056fea264697066735822122072103e00d8718137abb5b8b1ddfe6ba5344b2cb6789d84078c2a47c6bdb323dd64736f6c634300080f0033496e697469616c697a61626c653a20636f6e747261637420697320616c726561496e697469616c697a61626c653a20636f6e7472616374206973206e6f7420690000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

Deployed Bytecode

0x6080604052600436106102885760003560e01c80637573af8e1161015a578063ba730e53116100c1578063d1a93d181161007a578063d1a93d18146107df578063d5bb7f67146107f2578063d7c9423014610812578063d992f1ab14610832578063ef0c256914610847578063fcc2c0781461086757600080fd5b8063ba730e5314610732578063be1125b514610752578063c157253d14610767578063c688d69314610787578063cd3f29e9146107a7578063cfdc8b02146107cc57600080fd5b8063adc8d09d11610113578063adc8d09d14610698578063ae5b102e146106b8578063ae60bda4146106d8578063ae682e2e146106f0578063b1c6ffcf14610708578063b32820e91461071f57600080fd5b80637573af8e146105e95780638db462fc146105fe5780639477d85d1461061c578063974236cb1461063c5780639e59e2f214610653578063a58379a11461067857600080fd5b8063427e3618116101fe57806358ab4e90116101b757806358ab4e90146105185780635cf4ee91146105445780635f100c5c14610564578063695639b7146105845780636b4ed02a14610599578063725f3626146105b957600080fd5b8063427e3618146104695780634377053b1461047c578063442767331461048f5780634526bc3f146104c55780634ba8ae81146104da57806353b73799146104fa57600080fd5b806319786af81161025057806319786af81461037657806320357321146103ac578063252a1ccb146103c15780632b521416146103e1578063357d7ec91461040357806337e36f831461043757600080fd5b806305c3cf6a1461028d57806306ae0df9146102af57806308d4db141461030857806308f97dd81461033657806319224cb414610356575b600080fd5b34801561029957600080fd5b506102ad6102a8366004611b38565b610887565b005b3480156102bb57600080fd5b506040805180820182526000808252602091820152815180830183526033546001600160a01b03168082526034549183019182528351908152905191810191909152015b60405180910390f35b34801561031457600080fd5b50610328610323366004611b68565b610895565b6040519081526020016102ff565b34801561034257600080fd5b50610328610351366004611b68565b6108af565b34801561036257600080fd5b50610328610371366004611b81565b6108f5565b34801561038257600080fd5b50610328610391366004611bcc565b6001600160a01b031660009081526039602052604090205490565b3480156103b857600080fd5b50603854610328565b3480156103cd57600080fd5b506102ad6103dc366004611bcc565b610995565b3480156103ed57600080fd5b5030600090815260016020526040902054610328565b34801561040f57600080fd5b50610418610a34565b604080516001600160a01b0390931683526020830191909152016102ff565b34801561044357600080fd5b506036546001600160a01b03165b6040516001600160a01b0390911681526020016102ff565b6102ad610477366004611b38565b610a6a565b6102ad61048a366004611b38565b610af6565b34801561049b57600080fd5b506103286104aa366004611bcc565b6001600160a01b031660009081526001602052604090205490565b3480156104d157600080fd5b506102ad610b00565b3480156104e657600080fd5b506102ad6104f5366004611b68565b610c5a565b34801561050657600080fd5b506037546001600160401b0316610328565b34801561052457600080fd5b5061052f6202000081565b60405163ffffffff90911681526020016102ff565b34801561055057600080fd5b5061032861055f366004611be9565b610c67565b34801561057057600080fd5b506102ad61057f366004611c23565b610c86565b34801561059057600080fd5b50610418610c91565b3480156105a557600080fd5b506103286105b4366004611b68565b610ccf565b3480156105c557600080fd5b506105d96105d4366004611b68565b610d0c565b60405190151581526020016102ff565b3480156105f557600080fd5b50610451610d28565b34801561060a57600080fd5b506035546001600160a01b0316610451565b34801561062857600080fd5b50610328610637366004611be9565b610d54565b34801561064857600080fd5b5061052f6208000081565b34801561065f57600080fd5b50603654600160a01b90046001600160401b0316610328565b34801561068457600080fd5b50610328610693366004611b81565b610d69565b3480156106a457600080fd5b506102ad6106b3366004611b68565b610dfb565b3480156106c457600080fd5b506102ad6106d3366004611c3f565b610e04565b3480156106e457600080fd5b50610328600160fe1b81565b3480156106fc57600080fd5b50610328600160ff1b81565b34801561071457600080fd5b5061052f6201000081565b6102ad61072d366004611b68565b610e89565b34801561073e57600080fd5b5061032861074d366004611b68565b610e92565b34801561075e57600080fd5b50603a54610328565b34801561077357600080fd5b50610328610782366004611be9565b610ea6565b34801561079357600080fd5b506105d96107a2366004611c3f565b610eb2565b3480156107b357600080fd5b50603554600160a01b90046001600160401b0316610328565b6102ad6107da366004611c82565b610ed4565b6102ad6107ed366004611b68565b610fca565b3480156107fe57600080fd5b506102ad61080d366004611b68565b610fd4565b34801561081e57600080fd5b506102ad61082d366004611b38565b610fde565b34801561083e57600080fd5b506104186111b2565b34801561085357600080fd5b506102ad610862366004611d7b565b6111f0565b34801561087357600080fd5b506105d9610882366004611b68565b6112c9565b6108918282610fde565b5050565b60006108a96108a360385490565b83610ea6565b92915050565b6000806108ba610c91565b91505060006108c76111b2565b91505060006108d4610a34565b9150506108ec6108e360385490565b86858585610d69565b95945050505050565b6000806109028787610ea6565b90506000670de0b6b3a76400006109198784611dbe565b6109239190611ddd565b90506000670de0b6b3a764000061093a8785611dbe565b6109449190611ddd565b90506000670de0b6b3a764000061095b8786611dbe565b6109659190611ddd565b905080826109738587611dff565b61097d9190611dff565b6109879190611dff565b9a9950505050505050505050565b6109a1620100006112c9565b6109c65760405162461bcd60e51b81526004016109bd90611e17565b60405180910390fd5b603554604080516001600160a01b03928316815291831660208301527f56193c420686fede45e4df7337054c2c9d6e74ea3d4773ab0d4f18197b08142d910160405180910390a1603580546001600160a01b0319166001600160a01b0392909216919091179055565b905090565b600080610a3f610d28565b91506001600160a01b03821615610a61576037546001600160401b0316610a64565b60005b90509091565b6000610a7560385490565b1180610a90575033610a85610d28565b6001600160a01b0316145b610aec5760405162461bcd60e51b815260206004820152602760248201527f6f6e6c7920746865206973737565722063616e206275792074686520666972736044820152667420736861726560c81b60648201526084016109bd565b61089182826115e3565b6108918282610a6a565b610b0c620200006112c9565b610b285760405162461bcd60e51b81526004016109bd90611e17565b6036546001600160a01b0316151580610b525750603654600160a01b90046001600160401b031615155b610b8c5760405162461bcd60e51b815260206004820152600b60248201526a1b9bdd08195b98589b195960aa1b60448201526064016109bd565b6035546036547fbf3d73ac4d2b03dfb17b5ef50c84465692647f0843b94f9ebc4062034a6f8e0b916001600160401b03600160a01b91829004811692610bd59290041682611e3e565b604080516001600160401b0393841681529290911660208301520160405180910390a1603654603580546001600160401b03600160a01b93849004811693601492610c2592869290910416611e3e565b82546001600160401b039182166101009390930a928302919092021990911617905550603680546001600160e01b0319169055565b610c648133610fde565b50565b60006002610c7584846112e5565b610c7f9190611ddd565b9392505050565b610c648160006111f0565b600080610ca66035546001600160a01b031690565b91506001600160a01b03821615610a6157603554600160a01b90046001600160401b0316610a64565b600080610cda610c91565b9150506000610ce76111b2565b9150506000610cf4610a34565b9150506108ec610d0360385490565b868585856108f5565b306000908152600160205260408120546108a9905b8316831490565b604080518082019091526033546001600160a01b031681526034546020820152600090610a2f90611413565b6000610c7f610d638385611e69565b83610c67565b600080610d768787610d54565b90506000670de0b6b3a7640000610d8d8784611dbe565b610d979190611ddd565b90506000670de0b6b3a7640000610dae8785611dbe565b610db89190611ddd565b90506000670de0b6b3a7640000610dcf8786611dbe565b610dd99190611ddd565b90508082610de78587611e69565b610df19190611e69565b6109879190611e69565b610c6481610c5a565b610e11600160ff1b6112c9565b610e2d5760405162461bcd60e51b81526004016109bd90611e17565b6108918282610e8433610e55876001600160a01b031660009081526001602052604090205490565b6001600160a01b0391909116600090815260016020526040902054600019808818821618908716919091171690565b611774565b610c6481610fca565b60006108a9610ea060385490565b83610d54565b6000610c7f8383610c67565b6001600160a01b038216600090815260016020526040812054610c7f90610d21565b600054610100900460ff16610eef5760005460ff1615610ef3565b303b155b610f565760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b60648201526084016109bd565b600054610100900460ff16158015610f78576000805461ffff19166101011790555b610f878a8a8a8a8a8a8a6117ce565b8215610f9c57610f9783836115e3565b610fac565b3415610fac57610fac33346112db565b8015610fbe576000805461ff00191690555b50505050505050505050565b610c648133610a6a565b610c643082610e04565b6000610fe960385490565b905082811161103a5760405162461bcd60e51b815260206004820152601a60248201527f63616e6e6f742073656c6c20746865206c61737420736861726500000000000060448201526064016109bd565b33600090815260396020526040902054838110156110905760405162461bcd60e51b8152602060048201526013602482015272696e73756666696369656e742073686172657360681b60448201526064016109bd565b61109a8482611e69565b336000908152603960205260409020556110b48483611e69565b60388190556000906110c69086610c67565b905060006110d382611888565b9250505060006110e683600089336118e8565b925050506000806110f685611a19565b92505091506111308184868861110c9190611e69565b6111169190611e69565b6111209190611e69565b6001600160a01b038a16906112db565b61113985611a26565b603854604080518b815260208101889052908101869052606081018590526080810183905260a08101919091526000906001600160a01b0380851691908b16907f952ff8d90add9fdeaeb478102d54441cf0cc0cbe53b1d99e51f747cdc8379e549060c0015b60405180910390a4505050505050505050565b6000806111c76036546001600160a01b031690565b91506001600160a01b03821615610a6157603654600160a01b90046001600160401b0316610a64565b6111fc620800006112c9565b6112185760405162461bcd60e51b81526004016109bd90611e17565b7f4b356af3260ac232dff1ad2c4ce0f2d58640a02df64c6b965db54e527ce40c886033838360405161124c93929190611e80565b60405180910390a18160336112618282611ecd565b50506001600160a01b0381161561089157806001600160a01b0316637b80be6b6040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156112ad57600080fd5b505af11580156112c1573d6000803e3d6000fd5b505050505050565b60006108a93383610eb2565b3b151590565b6108918282611a40565b60008083156113405760066112fb600186611e69565b611306906002611dbe565b611311906001611dff565b8561131d600182611e69565b6113279190611dbe565b6113319190611dbe565b61133b9190611ddd565b611343565b60005b9050600084158015611356575060018411155b6113cb57600660016113688688611dff565b6113729190611e69565b61137d906002611dbe565b611388906001611dff565b6113928688611dff565b600161139e888a611dff565b6113a89190611e69565b6113b29190611dbe565b6113bc9190611dbe565b6113c69190611ddd565b6113ce565b60005b905060006113dc8383611e69565b9050613e806113f382670de0b6b3a7640000611dbe565b6113fd9190611ddd565b9695505050505050565b6000610c7f8383611a8d565b80516000903b156115db5760008083600001516001600160a01b0316611324634f558e7960e01b866020015160405160240161145191815260200190565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b031990941693909317909252905161148f9190611f03565b6000604051808303818686fa925050503d80600081146114cb576040519150601f19603f3d011682016040523d82523d6000602084013e6114d0565b606091505b509150915081156114fd57808060200190518101906114ef9190611f3e565b6114fd575060009392505050565b505060008083600001516001600160a01b0316611324636352211e60e01b866020015160405160240161153291815260200190565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b03199094169390931790925290516115709190611f03565b6000604051808303818686fa925050503d80600081146115ac576040519150601f19603f3d011682016040523d82523d6000602084013e6115b1565b606091505b509150915081156115d857808060200190518101906115d09190611f60565b949350505050565b50505b506000919050565b60006115ee60385490565b6001600160a01b03831660009081526039602052604081208054929350859290919061161b908490611dff565b9091555061162b90508382611dff565b603855600061163a8285610c67565b9050600061164782611888565b92505050600061165a83600188886118e8565b9250505060008061166a85611a19565b925050915060008184868861167f9190611dff565b6116899190611dff565b6116939190611dff565b9050803410156116e55760405162461bcd60e51b815260206004820152601b60248201527f696e73756666696369656e742076616c756520737570706c696564000000000060448201526064016109bd565b80341115611701576117016116fa8234611e69565b33906112db565b61170a86611a26565b603854604080518b815260208101899052908101879052606081018690526080810184905260a08101919091526001906001600160a01b0380861691908b16907f952ff8d90add9fdeaeb478102d54441cf0cc0cbe53b1d99e51f747cdc8379e549060c00161119f565b6001600160a01b03831660008181526001602090815260409182902084905581518581529081018490527fe9be537308880e0f56b7d7cfd7abf85f14c4934486d138f848b92a0cbaf659b4910160405180910390a2505050565b600054610100900460ff166117f55760405162461bcd60e51b81526004016109bd90611f7d565b6117fe87611aef565b8551603380546001600160a01b0319166001600160a01b03928316179055602090960151603455603580549587166001600160e01b031996871617600160a01b6001600160401b0396871681029190911790915560368054949097169390951692909217908316909302929092179092556037805467ffffffffffffffff19169190921617905550565b6000806000611895610c91565b9093509150670de0b6b3a76400006118ad8386611dbe565b6118b79190611ddd565b905080158015906118d857506118d66001600160a01b03841682611407565b155b156118e1575060005b9193909250565b60008060006118f56111b2565b9093509150670de0b6b3a764000061190d8389611dbe565b6119179190611ddd565b90506001600160a01b0383161580159061193057508415155b15611a0f57604080516001600160a01b0386166020820152871515918101919091526060810186905260009060800160405160208183030381529060405290506000846001600160a01b0316838360405161198b9190611f03565b60006040518083038185875af1925050503d80600081146119c8576040519150601f19603f3d011682016040523d82523d6000602084013e6119cd565b606091505b5050905080611a0c5760405162461bcd60e51b815260206004820152600b60248201526a1cde5b98c819985a5b195960aa1b60448201526064016109bd565b50505b9450945094915050565b6000806000611895610a34565b80603a6000828254611a389190611dff565b909155505050565b611a4a8282611a8d565b6108915760405162461bcd60e51b81526020600482015260146024820152733330b4b632b2103a379039b2b7321032ba3432b960611b60448201526064016109bd565b600080836001600160a01b031661132484604051600060405180830381858888f193505050503d8060008114611adf576040519150601f19603f3d011682016040523d82523d6000602084013e611ae4565b606091505b509095945050505050565b600054610100900460ff16611b165760405162461bcd60e51b81526004016109bd90611f7d565b610c648160001980611774565b6001600160a01b0381168114610c6457600080fd5b60008060408385031215611b4b57600080fd5b823591506020830135611b5d81611b23565b809150509250929050565b600060208284031215611b7a57600080fd5b5035919050565b600080600080600060a08688031215611b9957600080fd5b505083359560208501359550604085013594606081013594506080013592509050565b8035611bc781611b23565b919050565b600060208284031215611bde57600080fd5b8135610c7f81611b23565b60008060408385031215611bfc57600080fd5b50508035926020909101359150565b600060408284031215611c1d57600080fd5b50919050565b600060408284031215611c3557600080fd5b610c7f8383611c0b565b60008060408385031215611c5257600080fd5b8235611c5d81611b23565b946020939093013593505050565b80356001600160401b0381168114611bc757600080fd5b6000806000806000806000806000898b03610140811215611ca257600080fd5b8a35611cad81611b23565b99506040601f1982011215611cc157600080fd5b50604051604081018181106001600160401b0382111715611cf257634e487b7160e01b600052604160045260246000fd5b60405260208b0135611d0381611b23565b815260408b013560208201529750611d1d60608b01611bbc565b9650611d2b60808b01611c6b565b9550611d3960a08b01611bbc565b9450611d4760c08b01611c6b565b9350611d5560e08b01611c6b565b92506101008a01359150611d6c6101208b01611bbc565b90509295985092959850929598565b60008060608385031215611d8e57600080fd5b611d988484611c0b565b91506040830135611b5d81611b23565b634e487b7160e01b600052601160045260246000fd5b6000816000190483118215151615611dd857611dd8611da8565b500290565b600082611dfa57634e487b7160e01b600052601260045260246000fd5b500490565b60008219821115611e1257611e12611da8565b500190565b6020808252600d908201526c1858d8d95cdcc819195b9a5959609a1b604082015260600190565b60006001600160401b03808316818516808303821115611e6057611e60611da8565b01949350505050565b600082821015611e7b57611e7b611da8565b500390565b83546001600160a01b0390811682526001850154602083015260a08201908435611ea981611b23565b81811660408501525060208501356060840152808416608084015250949350505050565b8135611ed881611b23565b81546001600160a01b0319166001600160a01b03919091161781556020919091013560019190910155565b6000825160005b81811015611f245760208186018101518583015201611f0a565b81811115611f33576000828501525b509190910192915050565b600060208284031215611f5057600080fd5b81518015158114610c7f57600080fd5b600060208284031215611f7257600080fd5b8151610c7f81611b23565b6020808252602b908201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960408201526a6e697469616c697a696e6760a81b60608201526080019056fea264697066735822122072103e00d8718137abb5b8b1ddfe6ba5344b2cb6789d84078c2a47c6bdb323dd64736f6c634300080f0033

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

0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

-----Decoded View---------------
Arg [0] : _owner (address): 0x0000000000000000000000000000000000000000
Arg [1] : _sharesSubject (tuple): System.Collections.Generic.List`1[Nethereum.ABI.FunctionEncoding.ParameterOutput]
Arg [2] : _protocolFeeDestination (address): 0x0000000000000000000000000000000000000000
Arg [3] : _protocolFeePercent (uint64): 0
Arg [4] : _holdersFeeDestination (address): 0x0000000000000000000000000000000000000000
Arg [5] : _holdersFeePercent (uint64): 0
Arg [6] : _subjectFeePercent (uint64): 0
Arg [7] : _amount (uint256): 0
Arg [8] : _beneficiary (address): 0x0000000000000000000000000000000000000000

-----Encoded View---------------
10 Constructor Arguments found :
Arg [0] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [1] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [2] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [3] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [4] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [5] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [6] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [7] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [8] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [9] : 0000000000000000000000000000000000000000000000000000000000000000


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

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