Source Code
Overview
ETH Balance
0 ETH
Eth Value
$0.00View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Contract Name:
ETHKeys
Compiler Version
v0.8.15+commit.e14f2714
Contract Source Code (Solidity Standard Json-Input format)
// 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); } }
// 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); } }
{ "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
- No Contract Security Audit Submitted- Submit Audit Here
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"}]
Contract Creation Code
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
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 35 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
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.