BaseBase
0x00...6cAf
OpenSocial Protocol Profile

OpenSocial Protocol Profile

OSPP

收藏品
大小
570,013
收藏品
所有者
570,013
100% 独特的所有者
此合同的源代码已经过验证!
合同元数据
编译器
0.8.20+commit.a1b79de6
语言
Solidity
合同源代码
文件 1 的 124:AccessControlUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/AccessControl.sol)

pragma solidity ^0.8.20;

import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol";
import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol";
import {ERC165Upgradeable} from "../utils/introspection/ERC165Upgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.sol";

/**
 * @dev Contract module that allows children to implement role-based access
 * control mechanisms. This is a lightweight version that doesn't allow enumerating role
 * members except through off-chain means by accessing the contract event logs. Some
 * applications may benefit from on-chain enumerability, for those cases see
 * {AccessControlEnumerable}.
 *
 * Roles are referred to by their `bytes32` identifier. These should be exposed
 * in the external API and be unique. The best way to achieve this is by
 * using `public constant` hash digests:
 *
 * ```solidity
 * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
 * ```
 *
 * Roles can be used to represent a set of permissions. To restrict access to a
 * function call, use {hasRole}:
 *
 * ```solidity
 * function foo() public {
 *     require(hasRole(MY_ROLE, msg.sender));
 *     ...
 * }
 * ```
 *
 * Roles can be granted and revoked dynamically via the {grantRole} and
 * {revokeRole} functions. Each role has an associated admin role, and only
 * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
 *
 * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
 * that only accounts with this role will be able to grant or revoke other
 * roles. More complex role relationships can be created by using
 * {_setRoleAdmin}.
 *
 * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
 * grant and revoke this role. Extra precautions should be taken to secure
 * accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
 * to enforce additional security measures for this role.
 */
abstract contract AccessControlUpgradeable is Initializable, ContextUpgradeable, IAccessControl, ERC165Upgradeable {
    struct RoleData {
        mapping(address account => bool) hasRole;
        bytes32 adminRole;
    }

    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;


    /// @custom:storage-location erc7201:openzeppelin.storage.AccessControl
    struct AccessControlStorage {
        mapping(bytes32 role => RoleData) _roles;
    }

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.AccessControl")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant AccessControlStorageLocation = 0x02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800;

    function _getAccessControlStorage() private pure returns (AccessControlStorage storage $) {
        assembly {
            $.slot := AccessControlStorageLocation
        }
    }

    /**
     * @dev Modifier that checks that an account has a specific role. Reverts
     * with an {AccessControlUnauthorizedAccount} error including the required role.
     */
    modifier onlyRole(bytes32 role) {
        _checkRole(role);
        _;
    }

    function __AccessControl_init() internal onlyInitializing {
    }

    function __AccessControl_init_unchained() internal onlyInitializing {
    }
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) public view virtual returns (bool) {
        AccessControlStorage storage $ = _getAccessControlStorage();
        return $._roles[role].hasRole[account];
    }

    /**
     * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()`
     * is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier.
     */
    function _checkRole(bytes32 role) internal view virtual {
        _checkRole(role, _msgSender());
    }

    /**
     * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`
     * is missing `role`.
     */
    function _checkRole(bytes32 role, address account) internal view virtual {
        if (!hasRole(role, account)) {
            revert AccessControlUnauthorizedAccount(account, role);
        }
    }

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) public view virtual returns (bytes32) {
        AccessControlStorage storage $ = _getAccessControlStorage();
        return $._roles[role].adminRole;
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     *
     * May emit a {RoleGranted} event.
     */
    function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
        _grantRole(role, account);
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     *
     * May emit a {RoleRevoked} event.
     */
    function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
        _revokeRole(role, account);
    }

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been revoked `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `callerConfirmation`.
     *
     * May emit a {RoleRevoked} event.
     */
    function renounceRole(bytes32 role, address callerConfirmation) public virtual {
        if (callerConfirmation != _msgSender()) {
            revert AccessControlBadConfirmation();
        }

        _revokeRole(role, callerConfirmation);
    }

    /**
     * @dev Sets `adminRole` as ``role``'s admin role.
     *
     * Emits a {RoleAdminChanged} event.
     */
    function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
        AccessControlStorage storage $ = _getAccessControlStorage();
        bytes32 previousAdminRole = getRoleAdmin(role);
        $._roles[role].adminRole = adminRole;
        emit RoleAdminChanged(role, previousAdminRole, adminRole);
    }

    /**
     * @dev Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleGranted} event.
     */
    function _grantRole(bytes32 role, address account) internal virtual returns (bool) {
        AccessControlStorage storage $ = _getAccessControlStorage();
        if (!hasRole(role, account)) {
            $._roles[role].hasRole[account] = true;
            emit RoleGranted(role, account, _msgSender());
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Attempts to revoke `role` to `account` and returns a boolean indicating if `role` was revoked.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleRevoked} event.
     */
    function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {
        AccessControlStorage storage $ = _getAccessControlStorage();
        if (hasRole(role, account)) {
            $._roles[role].hasRole[account] = false;
            emit RoleRevoked(role, account, _msgSender());
            return true;
        } else {
            return false;
        }
    }
}
合同源代码
文件 2 的 124:ActivityConditionBase.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

import {IActivityCondition} from '../../interfaces/IActivityCondition.sol';
import {IERC165} from '@openzeppelin/contracts/interfaces/IERC165.sol';
import {ConditionContext} from 'contracts/core/base/ConditionContext.sol';

abstract contract ActivityConditionBase is ConditionContext, IERC165, IActivityCondition {
    constructor(
        address osp,
        address compositeCondition
    ) ConditionContext(osp, compositeCondition) {}

    function initializeCommunityActivityCondition(
        uint256 communityId,
        bytes calldata data
    ) external onlyOspOrCompositeCondition {
        _initializeCommunityActivityCondition(communityId, data);
    }

    function processActivity(
        uint256 communityId,
        address profileOwner,
        uint256 profileId
    ) external onlyOspOrCompositeCondition {
        _processActivity(communityId, profileOwner, profileId);
    }

    function _initializeCommunityActivityCondition(
        uint256 communityId,
        bytes calldata data
    ) internal virtual;

    function _processActivity(
        uint256 communityId,
        address profileOwner,
        uint256 profileId
    ) internal virtual;

    function supportsInterface(bytes4 interfaceId) public pure virtual override returns (bool) {
        return
            interfaceId == type(IActivityCondition).interfaceId ||
            interfaceId == type(IERC165).interfaceId;
    }
}
合同源代码
文件 3 的 124:ActivityExtensionBase.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

import {OspContext} from './OspContext.sol';
import {IERC165} from '@openzeppelin/contracts/utils/introspection/IERC165.sol';
import {IActivityExtension} from '../../interfaces/IActivityExtension.sol';
import {IActivityExtensionQueryable} from '../../interfaces/IActivityExtensionQueryable.sol';

abstract contract ActivityExtensionBase is OspContext, IActivityExtensionQueryable, IERC165 {
    constructor(address osp) OspContext(osp) {}

    function initializeActivityExtension(
        uint256 profileId,
        uint256 contentId,
        bytes calldata initData
    ) external payable onlyOsp {
        _initializeActivityExtension(profileId, contentId, initData);
    }

    function _initializeActivityExtension(
        uint256 profileId,
        uint256 contentId,
        bytes calldata initData
    ) internal virtual;

    function supportsInterface(bytes4 interfaceId) external pure override returns (bool) {
        return
            interfaceId == type(IActivityExtension).interfaceId ||
            interfaceId == type(IActivityExtensionQueryable).interfaceId ||
            interfaceId == type(IERC165).interfaceId;
    }

    function getExtensionData(
        uint256 profileId,
        uint256 contentId
    ) external view virtual override returns (bytes memory) {
        return '';
    }
}
合同源代码
文件 4 的 124:Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)

pragma solidity ^0.8.20;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev The ETH balance of the account is not enough to perform the operation.
     */
    error AddressInsufficientBalance(address account);

    /**
     * @dev There's no code at `target` (it is not a contract).
     */
    error AddressEmptyCode(address target);

    /**
     * @dev A call to an address target failed. The target may have reverted.
     */
    error FailedInnerCall();

    /**
     * @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://consensys.net/diligence/blog/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.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        if (address(this).balance < amount) {
            revert AddressInsufficientBalance(address(this));
        }

        (bool success, ) = recipient.call{value: amount}("");
        if (!success) {
            revert FailedInnerCall();
        }
    }

    /**
     * @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 or custom error, it is bubbled
     * up by this function (like regular Solidity function calls). However, if
     * the call reverted with no returned reason, this function reverts with a
     * {FailedInnerCall} error.
     *
     * 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.
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0);
    }

    /**
     * @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`.
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        if (address(this).balance < value) {
            revert AddressInsufficientBalance(address(this));
        }
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
     * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
     * unsuccessful call.
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata
    ) internal view returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            // only check if target is a contract if the call was successful and the return data is empty
            // otherwise we already know that it was a contract
            if (returndata.length == 0 && target.code.length == 0) {
                revert AddressEmptyCode(target);
            }
            return returndata;
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
     * revert reason or with a default {FailedInnerCall} error.
     */
    function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            return returndata;
        }
    }

    /**
     * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
     */
    function _revert(bytes memory returndata) private pure {
        // 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
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert FailedInnerCall();
        }
    }
}
合同源代码
文件 5 的 124:CommunityAccountProxy.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

import {IGovernanceLogic} from '../core/logics/interfaces/IGovernanceLogic.sol';
import {Proxy} from '@openzeppelin/contracts/proxy/Proxy.sol';

contract CommunityAccountProxy is Proxy {
    address immutable OSP;

    constructor() {
        OSP = msg.sender;
    }

    function _implementation() internal view override returns (address) {
        return IGovernanceLogic(OSP).getERC6551AccountImpl();
    }
}
合同源代码
文件 6 的 124:CommunityCondBase.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

import {OspContext} from './OspContext.sol';
import {ICommunityCondition} from '../../interfaces/ICommunityCondition.sol';
import {IERC165} from '@openzeppelin/contracts/interfaces/IERC165.sol';

abstract contract CommunityCondBase is OspContext, ICommunityCondition, IERC165 {
    constructor(address osp) OspContext(osp) {}

    function processCreateCommunity(
        address to,
        string calldata handle,
        bytes calldata data
    ) external payable override onlyOsp {
        _processCreateCommunity(to, handle, data);
    }

    function supportsInterface(bytes4 interfaceId) external pure override returns (bool) {
        return
            interfaceId == type(ICommunityCondition).interfaceId ||
            interfaceId == type(IERC165).interfaceId;
    }

    function _processCreateCommunity(
        address to,
        string calldata handle,
        bytes calldata data
    ) internal virtual;
}
合同源代码
文件 7 的 124:CommunityLogic.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

import {OspLogicBase} from './OspLogicBase.sol';
import {ICommunityLogic} from './interfaces/ICommunityLogic.sol';
import {OspDataTypes} from 'contracts/libraries/OspDataTypes.sol';
import {OspErrors} from 'contracts/libraries/OspErrors.sol';
import {OspEvents} from 'contracts/libraries/OspEvents.sol';
import {Constants} from 'contracts/libraries/Constants.sol';
import {ICommunityNFT} from 'contracts/interfaces/ICommunityNFT.sol';
import {IJoinCondition} from 'contracts/interfaces/IJoinCondition.sol';
import {ICommunityCondition} from 'contracts/interfaces/ICommunityCondition.sol';
import {IActivityCondition} from 'contracts/interfaces/IActivityCondition.sol';
import {IViewPrivacyCondition} from 'contracts/interfaces/IViewPrivacyCondition.sol';
import {IERC6551Registry} from 'contracts/interfaces/IERC6551Registry.sol';
import {ERC6551Account} from 'contracts/core/base/ERC6551Account.sol';
import {JoinNFTProxy} from 'contracts/upgradeability/JoinNFTProxy.sol';
import {IJoinNFT} from 'contracts/interfaces/IJoinNFT.sol';

import {Strings} from '@openzeppelin/contracts/utils/Strings.sol';
import {IERC721} from '@openzeppelin/contracts/token/ERC721/IERC721.sol';
import {EnumerableSet} from '@openzeppelin/contracts/utils/structs/EnumerableSet.sol';

/**
 * @title CommunityLogic
 * @author OpenSocial Protocol
 * @dev CommunityLogic is a contract to interact with the community.
 */
contract CommunityLogic is OspLogicBase, ICommunityLogic {
    using Strings for uint256;
    using EnumerableSet for EnumerableSet.AddressSet;

    /*///////////////////////////////////////////////////////////////
                      Public functions
    //////////////////////////////////////////////////////////////*/

    /// @inheritdoc ICommunityLogic
    function createCommunity(
        OspDataTypes.CreateCommunityData calldata vars
    ) external payable override whenNotPaused returns (uint256) {
        return _createCommunity(vars);
    }

    function createCommunity(
        OspDataTypes.CreateCommunityData calldata vars,
        bytes calldata activityConditionInitCode,
        bytes calldata viewPrivacyConditionInitCode
    ) external payable override whenNotPaused returns (uint256) {
        uint256 communityId = _createCommunity(vars);
        _setActivityCondition(communityId, activityConditionInitCode);
        _setViewPrivacyCondition(communityId, viewPrivacyConditionInitCode);
        return communityId;
    }

    /// @inheritdoc ICommunityLogic
    function emitCommunityNFTTransferEvent(
        uint256 communityId,
        address from,
        address to
    ) external override {
        if (_getGovernanceStorage()._communityNFT != msg.sender) {
            revert OspErrors.NotCommunityNFT();
        }
        emit OspEvents.CommunityNFTTransferred(communityId, from, to, block.timestamp);
    }

    /// @inheritdoc ICommunityLogic
    function setJoinCondition(
        uint256 communityId,
        bytes calldata joinConditionInitCode
    ) external override whenNotPaused {
        _validateCallerHasCommunityRole(communityId, Constants.COMMUNITY_ADMIN_ACCESS);
        _setJoinCondition(communityId, joinConditionInitCode);
    }

    /// @inheritdoc ICommunityLogic
    function setActivityCondition(
        uint256 communityId,
        bytes calldata activityConditionInitCode
    ) external override whenNotPaused {
        _validateCallerHasCommunityRole(communityId, Constants.COMMUNITY_ADMIN_ACCESS);
        _setActivityCondition(communityId, activityConditionInitCode);
    }

    function setViewPrivacyCondition(
        uint256 communityId,
        bytes calldata viewPrivacyConditionInitCode
    ) external override whenNotPaused {
        _validateCallerHasCommunityRole(communityId, Constants.COMMUNITY_ADMIN_ACCESS);
        _setViewPrivacyCondition(communityId, viewPrivacyConditionInitCode);
    }

    function isViewPrivacyAllowed(
        uint256 communityId,
        uint256 profileId
    ) external view returns (bool) {
        return
            IViewPrivacyCondition(
                _getCommunityStorage()._communityById[communityId].viewPrivacyCondition
            ).isViewPrivacyAllowed(
                    communityId,
                    _getProfileStorage()._profileById[profileId].owner,
                    profileId
                );
    }

    /// @inheritdoc ICommunityLogic
    function updateTags(
        uint256 communityId,
        string[] calldata tags
    ) external override whenNotPaused {
        _validateCallerHasCommunityRole(communityId, Constants.COMMUNITY_ADMIN_ACCESS);
        if (tags.length > Constants.MAX_TAGS_NUMBER) revert OspErrors.TooManyTags();
        emit OspEvents.CommunityTagsUpdated(communityId, msg.sender, tags, block.timestamp);
    }

    /// @inheritdoc ICommunityLogic
    function setCommunityActivityExtensionWhitelist(
        uint256 communityId,
        address extension,
        bool isWhitelist
    ) external override whenNotPaused {
        _validateCallerHasCommunityRole(communityId, Constants.COMMUNITY_ADMIN_ACCESS);
        if (extension != address(0)) {
            _checkActivityExtension(extension);
        }
        EnumerableSet.AddressSet storage communityExtensionWhitelist = _getCommunityStorage()
            ._activityExtensionWhitelistByCommunity[communityId];
        if (
            isWhitelist
                ? communityExtensionWhitelist.add(extension)
                : communityExtensionWhitelist.remove(extension)
        ) {
            emit OspEvents.CommunityExtensionWhitelistSet(
                communityId,
                extension,
                isWhitelist,
                msg.sender,
                block.timestamp
            );
        }
    }

    /*///////////////////////////////////////////////////////////////
                       Public read functions
    /////////////////////////////////////////////////////////////*/
    function getCommunityTokenURI(
        uint256 communityId
    ) external view override returns (string memory) {
        string memory baseURI = _getGovernanceStorage()._baseURI;
        return
            bytes(baseURI).length > 0
                ? string(abi.encodePacked(baseURI, 'community/', communityId.toString()))
                : communityId.toString();
    }

    /// @inheritdoc ICommunityLogic
    function getCommunity(
        uint256 communityId
    ) external view override returns (OspDataTypes.CommunityStruct memory) {
        CommunityStorage storage communityStorage = _getCommunityStorage();
        OspDataTypes.CommunityStruct memory communityData = communityStorage._communityById[
            communityId
        ];
        return communityData;
    }

    /// @inheritdoc ICommunityLogic
    function getJoinNFT(uint256 communityId) external view override returns (address) {
        CommunityStorage storage communityStorage = _getCommunityStorage();
        return communityStorage._communityById[communityId].joinNFT;
    }

    /// @inheritdoc ICommunityLogic
    function getJoinCondition(uint256 communityId) external view override returns (address) {
        CommunityStorage storage communityStorage = _getCommunityStorage();
        return communityStorage._communityById[communityId].joinCondition;
    }

    /// @inheritdoc ICommunityLogic
    function getActivityCondition(uint256 communityId) external view override returns (address) {
        CommunityStorage storage communityStorage = _getCommunityStorage();
        return communityStorage._communityById[communityId].activityCondition;
    }

    /// @inheritdoc ICommunityLogic
    function getCommunityIdByHandle(string calldata handle) public view override returns (uint256) {
        return _getCommunityStorage()._communityIdByHandleHash[keccak256(bytes(handle))];
    }

    /// @inheritdoc ICommunityLogic
    function getCommunityAccount(uint256 communityId) public view override returns (address) {
        return
            IERC6551Registry(Constants.ERC6551_REGISTRY).account(
                _getGovernanceStorage()._communityAccountProxy,
                Constants.COMMUNITY_TBA_SALT,
                block.chainid,
                _getGovernanceStorage()._communityNFT,
                communityId
            );
    }

    /// @inheritdoc ICommunityLogic
    function getCommunityAccount(string calldata handle) external view override returns (address) {
        return getCommunityAccount(getCommunityIdByHandle(handle));
    }

    /// @inheritdoc ICommunityLogic
    function getCommunityActivityExtensionWhitelist(
        uint256 communityId
    ) external view override returns (address[] memory extensions) {
        EnumerableSet.AddressSet storage communityExtensionWhitelist = _getCommunityStorage()
            ._activityExtensionWhitelistByCommunity[communityId];
        uint256 length = communityExtensionWhitelist.length();
        extensions = new address[](length);
        for (uint256 i; i < length; i += 1) {
            extensions[i] = communityExtensionWhitelist.at(i);
        }
    }

    /*///////////////////////////////////////////////////////////////
                        Internal functions
    //////////////////////////////////////////////////////////////*/

    function _setJoinCondition(uint256 communityId, bytes calldata joinConditionInitCode) internal {
        address joinCondition = joinConditionInitCode.length == 0
            ? address(0)
            : _initJoinCondition(communityId, joinConditionInitCode);
        _getCommunityStorage()._communityById[communityId].joinCondition = joinCondition;
        emit OspEvents.JoinConditionSet(communityId, msg.sender, joinCondition, block.timestamp);
    }

    function _setActivityCondition(
        uint256 communityId,
        bytes calldata activityConditionInitCode
    ) internal {
        address activityCondition = activityConditionInitCode.length == 0
            ? address(0)
            : _initActivityCondition(communityId, activityConditionInitCode);
        _getCommunityStorage()._communityById[communityId].activityCondition = activityCondition;
        emit OspEvents.ActivityConditionSet(
            communityId,
            msg.sender,
            activityCondition,
            block.timestamp
        );
    }

    function _setViewPrivacyCondition(
        uint256 communityId,
        bytes calldata viewPrivacyConditionInitCode
    ) internal {
        address viewPrivacyCondition = viewPrivacyConditionInitCode.length == 0
            ? address(0)
            : _initViewPrivacyCondition(communityId, viewPrivacyConditionInitCode);
        _getCommunityStorage()
            ._communityById[communityId]
            .viewPrivacyCondition = viewPrivacyCondition;
        emit OspEvents.ViewPrivacyConditionSet(
            communityId,
            msg.sender,
            viewPrivacyCondition,
            block.timestamp
        );
    }

    function _createCommunity(
        OspDataTypes.CreateCommunityData calldata vars
    ) internal returns (uint256 communityId) {
        if (vars.tags.length > Constants.MAX_TAGS_NUMBER) revert OspErrors.TooManyTags();
        bool isSuperCreator = _hashRole(Constants.SUPER_COMMUNITY_CREATOR, msg.sender);
        if (!isSuperCreator) {
            _validateHasProfile(msg.sender);
            _validateCommunityCondition(
                msg.sender,
                vars.handle,
                vars.communityConditionAndData,
                msg.value
            );
        }
        // mint community NFT
        communityId = ICommunityNFT(_communityNFT()).mint(msg.sender);
        // validate and register handle
        _registerHandle(vars.handle, isSuperCreator, communityId);
        // deploy Join NFT
        address joinNFT = _deployJoinNFT(communityId, vars.handle);
        // deploy community TBA
        _deployCommunityTBA(communityId);
        _getCommunityStorage()._communityById[communityId].joinNFT = joinNFT;
        IJoinNFT(joinNFT).mint(msg.sender);
        //init join condition
        address joinCondition;
        if (vars.joinConditionInitCode.length != 0) {
            joinCondition = _initJoinCondition(communityId, vars.joinConditionInitCode);
            _getCommunityStorage()._communityById[communityId].joinCondition = joinCondition;
        }
        emit OspEvents.CommunityCreated(
            communityId,
            msg.sender,
            vars.handle,
            vars.communityConditionAndData,
            joinCondition,
            joinNFT,
            vars.tags,
            vars.ctx,
            block.timestamp
        );
    }

    function _initJoinCondition(
        uint256 communityId,
        bytes calldata initCode
    ) private returns (address joinCondition) {
        joinCondition = address(bytes20(initCode[:20]));
        _checkJoinCondition(joinCondition);
        bytes memory initCallData = initCode[20:];
        IJoinCondition(joinCondition).initializeCommunityJoinCondition(communityId, initCallData);
    }

    function _initActivityCondition(
        uint256 communityId,
        bytes calldata initCode
    ) private returns (address activityCondition) {
        activityCondition = address(bytes20(initCode[:20]));
        _checkActivityCondition(activityCondition);
        bytes memory initCallData = initCode[20:];
        IActivityCondition(activityCondition).initializeCommunityActivityCondition(
            communityId,
            initCallData
        );
    }

    function _initViewPrivacyCondition(
        uint256 communityId,
        bytes calldata initCode
    ) private returns (address viewPrivacyCondition) {
        viewPrivacyCondition = address(bytes20(initCode[:20]));
        _checkViewPrivacyCondition(viewPrivacyCondition);
        bytes memory initCallData = initCode[20:];
        IViewPrivacyCondition(viewPrivacyCondition).initializeCommunityViewPrivacyCondition(
            communityId,
            initCallData
        );
    }

    /**
     * @dev Deploys the given community's Join NFT contract.
     * @param communityId The token ID of the profile which Collect NFT should be deployed.
     * @param handle The profile's associated handle.
     *
     * @return address The address of the deployed Collect NFT contract.
     */
    function _deployJoinNFT(uint256 communityId, string memory handle) internal returns (address) {
        string memory idStr = communityId.toString();
        string memory name = string(abi.encodePacked(idStr, Constants.JOIN_NFT_NAME_SUFFIX));
        string memory symbol = string(abi.encodePacked(idStr, Constants.JOIN_NFT_SYMBOL_SUFFIX));

        bytes memory functionData = abi.encodeWithSelector(
            IJoinNFT.initialize.selector,
            communityId,
            name,
            symbol
        );
        address joinNFT = address(new JoinNFTProxy(functionData));
        emit OspEvents.JoinNFTDeployed(communityId, joinNFT, block.timestamp);
        return joinNFT;
    }

    function _deployCommunityTBA(uint256 communityId) internal returns (address) {
        if (_getGovernanceStorage()._erc6551AccountImpl == address(0)) {
            revert OspErrors.ERC6551AccountImplNotDeployed();
        }
        address tbaAccount = IERC6551Registry(Constants.ERC6551_REGISTRY).createAccount(
            _getGovernanceStorage()._communityAccountProxy,
            Constants.COMMUNITY_TBA_SALT,
            block.chainid,
            _communityNFT(),
            communityId
        );
        ERC6551Account(payable(tbaAccount)).initialize(communityId);
        return tbaAccount;
    }

    function _validateCommunityCondition(
        address creator,
        string calldata handle,
        bytes calldata communityConditionAndData,
        uint256 value
    ) internal {
        address communityCondition = address(bytes20(communityConditionAndData[:20]));
        _checkCommunityCondition(communityCondition);
        bytes memory callData = communityConditionAndData[20:];
        ICommunityCondition(communityCondition).processCreateCommunity{value: value}(
            creator,
            handle,
            callData
        );
    }

    function _validateHandle(
        string calldata handle,
        bool isSuperCreator,
        CommunityStorage storage communityStorage
    ) internal view returns (bytes32 hash) {
        bytes memory byteHandle = bytes(handle);
        bytes32 handleHash = keccak256(bytes(handle));
        if (byteHandle.length == 0 || byteHandle.length > Constants.MAX_COMMUNITY_NAME_LENGTH)
            revert OspErrors.HandleLengthInvalid();
        GovernanceStorage storage governanceStorage = _getGovernanceStorage();
        if (governanceStorage._reserveCommunityHandleHash[handleHash] && !isSuperCreator)
            revert OspErrors.HandleTaken();
        if (communityStorage._communityIdByHandleHash[handleHash] != 0)
            revert OspErrors.HandleTaken();
        uint256 byteHandleLength = byteHandle.length;
        for (uint256 i; i < byteHandleLength; ) {
            if (
                (byteHandle[i] < '0' ||
                    byteHandle[i] > 'z' ||
                    (byteHandle[i] > '9' && byteHandle[i] < 'a')) && byteHandle[i] != '_'
            ) revert OspErrors.HandleContainsInvalidCharacters();
            unchecked {
                ++i;
            }
        }
        return keccak256(byteHandle);
    }

    function _registerHandle(
        string calldata handle,
        bool isSuperCreator,
        uint256 communityId
    ) internal {
        CommunityStorage storage communityStorage = _getCommunityStorage();
        bytes32 handleHash = _validateHandle(handle, isSuperCreator, communityStorage);
        communityStorage._communityIdByHandleHash[handleHash] = communityId;
        communityStorage._communityById[communityId].handle = handle;
    }

    function _validateCallerHasCommunityRole(uint256 communityId, uint256 role) internal view {
        address joinNFT = _getCommunityStorage()._communityById[communityId].joinNFT;
        if (joinNFT == address(0)) revert OspErrors.InvalidCommunityId();
        if (IJoinNFT(joinNFT).hasRole(role, msg.sender)) return;
        revert OspErrors.JoinNFTUnauthorizedAccount();
    }

    function _communityNFT() internal view returns (address) {
        return _getGovernanceStorage()._communityNFT;
    }
}
合同源代码
文件 8 的 124:CommunityNFT.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

import './logics/interfaces/OspClient.sol';
import '../interfaces/ICommunityNFT.sol';
import '../libraries/OspErrors.sol';
import './base/OspNFTBase.sol';
import './base/IERC4906.sol';
import '@thirdweb-dev/contracts/extension/upgradeable/ContractMetadata.sol';
import '@openzeppelin/contracts-upgradeable/token/common/ERC2981Upgradeable.sol';

/**
 * @title CommunityNFT
 * @author OpenSocial Protocol
 * @dev This NFT contract is minted upon community is created.
 */
contract CommunityNFT is OspNFTBase, ContractMetadata, ICommunityNFT, IERC4906, ERC2981Upgradeable {
    address public immutable OSP;
    uint256 internal _tokenIdCounter;

    constructor(address osp) {
        if (osp == address(0)) revert OspErrors.InitParamsInvalid();
        OSP = osp;
    }

    function initialize(string calldata name, string calldata symbol) external {
        super._initialize(name, symbol);
    }

    function mint(address to) external override returns (uint256) {
        if (msg.sender != OSP) revert OspErrors.NotOSP();
        unchecked {
            uint256 tokenId = ++_tokenIdCounter;
            _mint(to, tokenId);
            return tokenId;
        }
    }

    function tokenURI(uint256 tokenId) public view override returns (string memory) {
        return ICommunityLogic(OSP).getCommunityTokenURI(tokenId);
    }

    /**
     * @dev Upon transfers, we emit the transfer event in the osp.
     */
    function _afterTokenTransfer(address from, address to, uint256 tokenId) internal override {
        ICommunityLogic(OSP).emitCommunityNFTTransferEvent(tokenId, from, to);
        super._afterTokenTransfer(from, to, tokenId);
    }

    function setTokenRoyalty(address receiver, uint96 feeNumerator) external {
        if (!OspClient(OSP).hasRole(Constants.GOVERNANCE, _msgSender())) {
            revert OspErrors.NotGovernance();
        }
        _setDefaultRoyalty(receiver, feeNumerator);
    }

    function updateMetadata() external override {
        emit BatchMetadataUpdate(1, type(uint256).max);
    }

    function burn(uint256 tokenId) public override {
        _update(address(this), tokenId, _msgSender());
    }

    function _canSetContractURI() internal view override returns (bool) {
        return OspClient(OSP).hasRole(Constants.GOVERNANCE, _msgSender());
    }

    function supportsInterface(
        bytes4 interfaceId
    ) public view virtual override(OspNFTBase, ERC2981Upgradeable) returns (bool) {
        return
            ERC2981Upgradeable.supportsInterface(interfaceId) ||
            OspNFTBase.supportsInterface(interfaceId);
    }
}
合同源代码
文件 9 的 124:CompositeActivityCond.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

import {IERC165} from '@openzeppelin/contracts/interfaces/IERC165.sol';
import {ERC165Checker} from '@openzeppelin/contracts/utils/introspection/ERC165Checker.sol';

import {OspContext} from 'contracts/core/base/OspContext.sol';
import {IActivityCondition} from 'contracts/interfaces/IActivityCondition.sol';
import {CondErrors} from '../libraries/CondErrors.sol';
import {CondHelpers} from '../libraries/CondHelpers.sol';
import {ICompositeActivityCond} from './interfaces/ICompositeActivityCond.sol';

/**
 * @title CompositeActivityCond
 * @author OpenSocial Protocol
 *
 * @notice This is the contract that implements the composable activity conditions.
 */
contract CompositeActivityCond is OspContext, ICompositeActivityCond, IERC165 {
    uint256 private constant MAX_CONDITIONS = 5;

    mapping(uint256 => CompositeData) internal _dataByCommunity;

    constructor(address osp) OspContext(osp) {}

    function initializeCommunityActivityCondition(
        uint256 communityId,
        bytes calldata data
    ) external override onlyOsp {
        (
            OperatorEnum operator,
            IActivityCondition[] memory conditions,
            bytes[] memory initDatas
        ) = abi.decode(data, (OperatorEnum, IActivityCondition[], bytes[]));
        uint256 length = conditions.length;
        if (length > MAX_CONDITIONS) revert CondErrors.TooManyConditions();
        if (length != initDatas.length) revert CondErrors.ArrayMismatch();
        for (uint256 i; i < length; ) {
            // check duplicated
            for (uint256 j; j < i; ) {
                if (conditions[i] == conditions[j]) {
                    revert CondErrors.ArrayDuplicated();
                }
                unchecked {
                    ++j;
                }
            }
            _checkActivityCondition(address(conditions[i]));
            conditions[i].initializeCommunityActivityCondition(communityId, initDatas[i]);
            unchecked {
                ++i;
            }
        }
        _dataByCommunity[communityId].operator = operator;
        _dataByCommunity[communityId].conditions = conditions;
    }

    function processActivity(
        uint256 communityId,
        address profileOwner,
        uint256 profileId
    ) external override onlyOsp {
        IActivityCondition[] storage conditions = _dataByCommunity[communityId].conditions;
        OperatorEnum operator = _dataByCommunity[communityId].operator;
        if (operator == OperatorEnum.AND) {
            for (uint256 i; i < conditions.length; i++) {
                conditions[i].processActivity(communityId, profileOwner, profileId);
            }
        } else {
            for (uint256 i; i < conditions.length; i++) {
                bool hasError = false;
                try conditions[i].processActivity(communityId, profileOwner, profileId) {} catch {
                    hasError = true;
                }
                if (!hasError) {
                    return;
                }
            }
            revert CondErrors.ActivityConditionNotMet();
        }
    }

    // @inheritdoc ICompositeActivityCond
    function getCompositeData(uint256 communityId) external view returns (CompositeData memory) {
        return _dataByCommunity[communityId];
    }

    // @inheritdoc IERC165
    function supportsInterface(bytes4 interfaceId) public pure override returns (bool) {
        return
            interfaceId == type(IActivityCondition).interfaceId ||
            interfaceId == type(IERC165).interfaceId ||
            interfaceId == type(ICompositeActivityCond).interfaceId;
    }

    function _checkActivityCondition(address condition) internal view {
        if (
            !OSP.isAppWhitelisted(condition) ||
            !ERC165Checker.supportsInterface(condition, type(IActivityCondition).interfaceId)
        ) revert CondErrors.ConditionNotWhitelisted();
    }
}
合同源代码
文件 10 的 124:CompositeViewPrivacyCond.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

import {IERC165} from '@openzeppelin/contracts/interfaces/IERC165.sol';
import {ERC165Checker} from '@openzeppelin/contracts/utils/introspection/ERC165Checker.sol';

import {OspContext} from 'contracts/core/base/OspContext.sol';
import {IViewPrivacyCondition} from 'contracts/interfaces/IViewPrivacyCondition.sol';
import {CondErrors} from '../libraries/CondErrors.sol';
import {CondHelpers} from '../libraries/CondHelpers.sol';
import {ICompositeViewPrivacyCond} from './interfaces/ICompositeViewPrivacyCond.sol';

/**
 * @title CompositeViewPrivacyCond
 * @author OpenSocial Protocol
 *
 * @notice This is the contract that implements the composable view privacy conditions.
 */
contract CompositeViewPrivacyCond is OspContext, ICompositeViewPrivacyCond, IERC165 {
    uint256 private constant MAX_CONDITIONS = 5;

    mapping(uint256 => CompositeData) internal _dataByCommunity;

    constructor(address osp) OspContext(osp) {}

    function initializeCommunityViewPrivacyCondition(
        uint256 communityId,
        bytes calldata data
    ) external override onlyOsp {
        (
            OperatorEnum operator,
            ICompositeViewPrivacyCond[] memory conditions,
            bytes[] memory initDatas
        ) = abi.decode(data, (OperatorEnum, ICompositeViewPrivacyCond[], bytes[]));
        uint256 length = conditions.length;
        if (length > MAX_CONDITIONS) revert CondErrors.TooManyConditions();
        if (length != initDatas.length) revert CondErrors.ArrayMismatch();
        for (uint256 i; i < length; ) {
            // check duplicated
            for (uint256 j; j < i; ) {
                if (conditions[i] == conditions[j]) {
                    revert CondErrors.ArrayDuplicated();
                }
                unchecked {
                    ++j;
                }
            }
            _checkViewPrivacyCondition(address(conditions[i]));
            conditions[i].initializeCommunityViewPrivacyCondition(communityId, initDatas[i]);
            unchecked {
                ++i;
            }
        }
        _dataByCommunity[communityId].operator = operator;
        _dataByCommunity[communityId].conditions = conditions;
    }

    function isViewPrivacyAllowed(
        uint256 communityId,
        address profileOwner,
        uint256 profileId
    ) external view override onlyOsp returns (bool) {
        IViewPrivacyCondition[] storage conditions = _dataByCommunity[communityId].conditions;
        OperatorEnum operator = _dataByCommunity[communityId].operator;
        if (operator == OperatorEnum.AND) {
            for (uint256 i; i < conditions.length; i++) {
                if (!conditions[i].isViewPrivacyAllowed(communityId, profileOwner, profileId)) {
                    return false;
                }
            }
            return true;
        } else {
            for (uint256 i; i < conditions.length; i++) {
                if (conditions[i].isViewPrivacyAllowed(communityId, profileOwner, profileId)) {
                    return true;
                }
            }
            return false;
        }
    }

    // @inheritdoc IViewPrivacyCondition
    function getCompositeData(uint256 communityId) external view returns (CompositeData memory) {
        return _dataByCommunity[communityId];
    }

    // @inheritdoc IERC165
    function supportsInterface(bytes4 interfaceId) public pure override returns (bool) {
        return
            interfaceId == type(IViewPrivacyCondition).interfaceId ||
            interfaceId == type(IERC165).interfaceId ||
            interfaceId == type(ICompositeViewPrivacyCond).interfaceId;
    }

    function _checkViewPrivacyCondition(address condition) internal view {
        if (
            !OSP.isAppWhitelisted(condition) ||
            !ERC165Checker.supportsInterface(condition, type(IViewPrivacyCondition).interfaceId)
        ) revert CondErrors.ConditionNotWhitelisted();
    }
}
合同源代码
文件 11 的 124:CondDataTypes.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

/**
 * @title CondDataTypes
 * @author OpenSocial Protocol
 *
 * @dev The CondDataTypes library contains data types used throughout the OpenSocial Protocol.
 */
library CondDataTypes {
    struct SlotNFTCondData {
        bool whitelist;
        uint256 minHandleLength;
    }

    struct FixedFeeCondData {
        uint256 price1Letter;
        uint256 price2Letter;
        uint256 price3Letter;
        uint256 price4Letter;
        uint256 price5Letter;
        uint256 price6Letter;
        uint256 price7ToMoreLetter;
        uint256 createStartTime;
    }
}
合同源代码
文件 12 的 124:CondErrors.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

library CondErrors {
    // array
    error ArrayMismatch();
    error ArrayDuplicated();

    // whitelist
    error ConditionNotWhitelisted();
    error NotWhitelisted();

    // common
    error ConditionDataMismatch();
    error InitParamsInvalid();

    // handle format
    error HandleLengthNotEnough();

    // slot nft
    error SlotNFTNotWhitelisted();
    error NotSlotNFTOwner();
    error SlotNFTAlreadyUsed();

    error NotCreateTime();
    error NotPresaleTime();
    error InvalidTicket();
    error InsufficientPayment();
    error SignatureInvalid();

    error JoinNFTTransferInvalid();
    error JoinInvalid();

    error AccessDenied();

    error ActivityConditionNotMet();

    // token
    error NotEnoughERC721Balance();

    error TooManyConditions();
}
合同源代码
文件 13 的 124:CondHelpers.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

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

library CondHelpers {
    /**
     * @dev Get the ETH price based on the length of the handle.
     * @param handle The handle to get the ETH price for.
     * @param fixedFeeCondData The fixed fee condition data.
     * @return The ETH price.
     */
    function getHandleETHPrice(
        string calldata handle,
        CondDataTypes.FixedFeeCondData memory fixedFeeCondData
    ) internal pure returns (uint256) {
        uint256 len = bytes(handle).length;
        uint256 ethPrice;
        if (len >= 7) {
            ethPrice = fixedFeeCondData.price7ToMoreLetter;
        } else if (len == 6) {
            ethPrice = fixedFeeCondData.price6Letter;
        } else if (len == 5) {
            ethPrice = fixedFeeCondData.price5Letter;
        } else if (len == 4) {
            ethPrice = fixedFeeCondData.price4Letter;
        } else if (len == 3) {
            ethPrice = fixedFeeCondData.price3Letter;
        } else if (len == 2) {
            ethPrice = fixedFeeCondData.price2Letter;
        } else {
            ethPrice = fixedFeeCondData.price1Letter;
        }
        return ethPrice;
    }
}
合同源代码
文件 14 的 124:ConditionContext.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

import '@openzeppelin/contracts/utils/Context.sol';
import 'contracts/libraries/OspErrors.sol';

abstract contract ConditionContext is Context {
    address immutable OSP;
    address immutable COMPOSITE_CONDITION;

    constructor(address osp, address compositeCondition) {
        if (osp == address(0)) revert OspErrors.InitParamsInvalid();
        if (compositeCondition == address(0)) revert OspErrors.InitParamsInvalid();
        OSP = osp;
        COMPOSITE_CONDITION = compositeCondition;
    }

    modifier onlyOsp() {
        if (_msgSender() != address(OSP)) revert OspErrors.NotOSP();
        _;
    }

    modifier onlyOspOrCompositeCondition() {
        if (_msgSender() != COMPOSITE_CONDITION && _msgSender() != address(OSP))
            revert OspErrors.InvalidSender();
        _;
    }

    modifier nonPayable() {
        if (msg.value != 0) revert OspErrors.InvalidValue();
        _;
    }
}
合同源代码
文件 15 的 124:Constants.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

/**
 * @title Constants
 * @author OpenSocial Protocol
 * @notice This library defines constants for the OpenSocial Protocol.
 */
library Constants {
    string internal constant FOLLOW_NFT_NAME_SUFFIX = "'s Follower";
    string internal constant FOLLOW_NFT_SYMBOL_SUFFIX = '-FOL';
    string internal constant JOIN_NFT_NAME_SUFFIX = "'s Member";
    string internal constant JOIN_NFT_SYMBOL_SUFFIX = '-MBR';
    uint8 internal constant MAX_HANDLE_LENGTH = 15;
    uint8 internal constant MIN_HANDLE_LENGTH = 4;
    uint8 internal constant MAX_COMMUNITY_NAME_LENGTH = 63;
    uint16 internal constant MAX_PROFILE_IMAGE_URI_LENGTH = 6000;
    address internal constant ERC6551_REGISTRY = 0x000000006551c19487814612e58FE06813775758;
    bytes32 internal constant COMMUNITY_TBA_SALT =
        0xd51dafa9227bb21dd4efbc739a5c611e802dd0ec1ef35b3dc8da5ad2dca64ae6;
    bytes32 internal constant APP_ADMIN = keccak256('APP_ADMIN');
    bytes32 internal constant GOVERNANCE = keccak256('GOVERNANCE');
    bytes32 internal constant OPERATION = keccak256('OPERATION');
    bytes32 internal constant STATE_ADMIN = keccak256('STATE_ADMIN');
    bytes32 internal constant SUPER_COMMUNITY_CREATOR = keccak256('SUPER_COMMUNITY_CREATOR');
    uint256 internal constant MAX_TAGS_NUMBER = 10;
    // community join nft roles
    uint256 internal constant COMMUNITY_NULL_ACCESS = 0x00000000;
    uint256 internal constant COMMUNITY_MODERATOR_ACCESS = 0x55555555;
    uint256 internal constant COMMUNITY_ADMIN_ACCESS = 0xaaaaaaaa;
    uint256 internal constant ROYALTY_DENOMINATOR = 10000;
}
合同源代码
文件 16 的 124:ContentLogic.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

import {IActivityExtension} from '../../interfaces/IActivityExtension.sol';
import {IReferenceCondition} from '../../interfaces/IReferenceCondition.sol';
import {IOpenReaction} from '../../interfaces/IOpenReaction.sol';
import {OspHelpers} from '../../libraries/OspHelpers.sol';
import {OspDataTypes} from '../../libraries/OspDataTypes.sol';
import {Constants} from '../../libraries/Constants.sol';
import {OspErrors} from '../../libraries/OspErrors.sol';
import {OspEvents} from '../../libraries/OspEvents.sol';
import {IContentLogic} from './interfaces/IContentLogic.sol';
import {OspLogicBase} from './OspLogicBase.sol';
import {Payment} from '../../libraries/Payment.sol';
import {IERC721} from '@openzeppelin/contracts/token/ERC721/IERC721.sol';
import {IActivityCondition} from '../../interfaces/IActivityCondition.sol';
import {EnumerableSet} from '@openzeppelin/contracts/utils/structs/EnumerableSet.sol';

/**
 * @title ContentLogic
 * @author OpenSocial Protocol
 */
contract ContentLogic is IContentLogic, OspLogicBase {
    using EnumerableSet for EnumerableSet.AddressSet;

    /*///////////////////////////////////////////////////////////////
                        Public functions
    //////////////////////////////////////////////////////////////*/

    /// @inheritdoc IContentLogic
    function createActivity(
        OspDataTypes.CreateActivityData calldata vars
    ) external payable override whenPublishingEnabled returns (uint256 contentId) {
        contentId = _createActivity(msg.sender, vars);
    }

    /// @inheritdoc IContentLogic
    function createActivityWithSig(
        OspDataTypes.CreateActivityData calldata vars,
        OspDataTypes.EIP712Signature calldata sig
    ) external whenPublishingEnabled returns (uint256 contentId) {
        unchecked {
            _validateRecoveredAddress(
                _calculateDigest(
                    keccak256(
                        abi.encode(
                            OspDataTypes.CREATE_ACTIVITY_WITH_SIG_TYPEHASH,
                            vars.profileId,
                            vars.communityId,
                            keccak256(bytes(vars.contentURI)),
                            keccak256(vars.extensionInitCode),
                            keccak256(vars.referenceConditionInitCode),
                            keccak256(vars.ctx),
                            _getProfileStorage()._sigNonces[sig.signer]++,
                            sig.deadline
                        )
                    )
                ),
                sig
            );
        }
        contentId = _createActivity(sig.signer, vars);
    }

    /// @inheritdoc IContentLogic
    function createComment(
        OspDataTypes.CreateCommentData calldata vars
    ) external payable whenPublishingEnabled returns (uint256 contentId) {
        _validateIsProfileOwner(msg.sender, vars.profileId);
        contentId = _createComment(vars);
    }

    /// @inheritdoc IContentLogic
    function createCommentWithSig(
        OspDataTypes.CreateCommentData calldata vars,
        OspDataTypes.EIP712Signature calldata sig
    ) external whenPublishingEnabled returns (uint256 contentId) {
        unchecked {
            _validateRecoveredAddress(
                _calculateDigest(
                    keccak256(
                        abi.encode(
                            OspDataTypes.CREATE_COMMENT_WITH_SIG_TYPEHASH,
                            vars.profileId,
                            vars.communityId,
                            keccak256(bytes(vars.contentURI)),
                            vars.referencedProfileId,
                            vars.referencedContentId,
                            keccak256(vars.referenceConditionInitCode),
                            keccak256(vars.referenceConditionData),
                            keccak256(vars.ctx),
                            _getProfileStorage()._sigNonces[sig.signer]++,
                            sig.deadline
                        )
                    )
                ),
                sig
            );
        }
        _validateIsProfileOwner(sig.signer, vars.profileId);
        contentId = _createComment(vars);
    }

    /// @inheritdoc IContentLogic
    function createOpenReaction(
        OspDataTypes.CreateOpenReactionData calldata vars
    ) external payable whenPublishingEnabled {
        _validateIsProfileOwner(msg.sender, vars.profileId);
        _createOpenReaction(vars);
    }

    /// @inheritdoc IContentLogic
    function createOpenReactionWithSig(
        OspDataTypes.CreateOpenReactionData calldata vars,
        OspDataTypes.EIP712Signature calldata sig
    ) external whenPublishingEnabled {
        unchecked {
            _validateRecoveredAddress(
                _calculateDigest(
                    keccak256(
                        abi.encode(
                            OspDataTypes.CREATE_OPEN_REACTION_WITH_SIG_TYPEHASH,
                            vars.profileId,
                            vars.communityId,
                            vars.referencedProfileId,
                            vars.referencedContentId,
                            keccak256(vars.reactionAndData),
                            keccak256(vars.referenceConditionData),
                            keccak256(vars.ctx),
                            _getProfileStorage()._sigNonces[sig.signer]++,
                            sig.deadline
                        )
                    )
                ),
                sig
            );
        }
        _validateIsProfileOwner(sig.signer, vars.profileId);
        _createOpenReaction(vars);
    }

    /// @inheritdoc IContentLogic
    function createMegaphone(
        OspDataTypes.CreateMegaphoneData calldata vars
    ) external payable whenPublishingEnabled returns (uint256 megaphoneId) {
        _validateIsProfileOwner(msg.sender, vars.profileId);
        return _createMegaphone(vars);
    }

    /*///////////////////////////////////////////////////////////////
                        Public Read functions
    //////////////////////////////////////////////////////////////*/

    /// @inheritdoc IContentLogic
    function getContentCount(uint256 profileId) external view override returns (uint256) {
        return _getProfileStorage()._profileById[profileId].contentCount;
    }

    /// @inheritdoc IContentLogic
    function getContent(
        uint256 profileId,
        uint256 contentId
    ) external view override returns (OspDataTypes.ContentStruct memory) {
        return _getContentStorage()._contentByIdByProfile[profileId][contentId];
    }

    /// @inheritdoc IContentLogic
    function getCommunityIdByContent(
        uint256 profileId,
        uint256 contentId
    ) external view returns (uint256) {
        return _getContentStorage()._contentByIdByProfile[profileId][contentId].communityId;
    }

    /*///////////////////////////////////////////////////////////////
                        Internal functions
    //////////////////////////////////////////////////////////////*/

    function _createActivity(
        address profileOwner,
        OspDataTypes.CreateActivityData calldata vars
    ) internal returns (uint256 contentId) {
        _validateIsProfileOwner(profileOwner, vars.profileId);
        if (bytes(vars.contentURI).length == 0) {
            revert OspErrors.InvalidContentURI();
        }
        uint256 profileId = vars.profileId;
        _validateCallerIsJoinCommunity(profileId, vars.communityId);
        _validateActivity(vars.communityId, profileOwner, vars.profileId);

        contentId = ++_getProfileStorage()._profileById[profileId].contentCount;
        OspDataTypes.ContentStruct storage activityContent = _getContentStorage()
            ._contentByIdByProfile[vars.profileId][contentId];
        //init activity
        activityContent.contentURI = vars.contentURI;
        activityContent.communityId = vars.communityId;

        address extension;
        if (vars.extensionInitCode.length != 0) {
            extension = _initActivityExtension(
                profileId,
                contentId,
                vars.extensionInitCode,
                msg.value
            );
            activityContent.extension = extension;
        }

        EnumerableSet.AddressSet storage communityExtensionWhitelist = _getCommunityStorage()
            ._activityExtensionWhitelistByCommunity[vars.communityId];
        bool enableExtensionWhitelist = communityExtensionWhitelist.length() != 0;
        if (enableExtensionWhitelist && !communityExtensionWhitelist.contains(extension)) {
            revert OspErrors.ExtensionNotCommunityWhitelisted();
        }
        if (!enableExtensionWhitelist && vars.communityId != 0 && extension != address(0)) {
            revert OspErrors.ExtensionNotCommunityWhitelisted();
        }

        address referenceCondition;
        if (vars.referenceConditionInitCode.length != 0) {
            referenceCondition = _initReferenceCondition(
                vars.profileId,
                contentId,
                vars.communityId,
                vars.referenceConditionInitCode
            );
            activityContent.referenceCondition = referenceCondition;
        }

        emit OspEvents.ActivityCreated(
            vars.profileId,
            contentId,
            vars.communityId,
            vars.contentURI,
            extension,
            referenceCondition,
            vars.ctx,
            block.timestamp
        );
    }

    function _createComment(
        OspDataTypes.CreateCommentData calldata vars
    ) internal returns (uint256 contentId) {
        if (bytes(vars.contentURI).length == 0) {
            revert OspErrors.InvalidContentURI();
        }
        _validateCallerIsJoinCommunity(vars.profileId, vars.communityId);
        _validateReferenced(
            vars.profileId,
            vars.communityId,
            vars.referencedProfileId,
            vars.referencedContentId,
            vars.referenceConditionData,
            msg.value
        );

        contentId = ++_getProfileStorage()._profileById[vars.profileId].contentCount;
        OspDataTypes.ContentStruct storage commentContent = _getContentStorage()
            ._contentByIdByProfile[vars.profileId][contentId];

        commentContent.referencedProfileId = vars.referencedProfileId;
        commentContent.referencedContentId = vars.referencedContentId;
        commentContent.contentURI = vars.contentURI;
        commentContent.communityId = vars.communityId;

        address referenceCondition;
        if (vars.referenceConditionInitCode.length != 0) {
            referenceCondition = _initReferenceCondition(
                vars.profileId,
                contentId,
                vars.communityId,
                vars.referenceConditionInitCode
            );
            commentContent.referenceCondition = referenceCondition;
        }

        emit OspEvents.CommentCreated(
            vars.profileId,
            contentId,
            vars.communityId,
            vars.contentURI,
            vars.referencedProfileId,
            vars.referencedContentId,
            referenceCondition,
            vars.ctx,
            block.timestamp
        );
    }

    function _createMegaphone(
        OspDataTypes.CreateMegaphoneData calldata vars
    ) internal returns (uint256 megaphoneId) {
        if (vars.tags.length > Constants.MAX_TAGS_NUMBER) revert OspErrors.TooManyTags();

        ContentStorage storage contentStorage = _getContentStorage();

        if (
            vars.referencedContentId == 0 ||
            _getProfileStorage()._profileById[vars.referencedProfileId].contentCount <
            vars.referencedContentId
        ) {
            revert OspErrors.ContentDoesNotExist();
        }

        if (
            contentStorage
            ._contentByIdByProfile[vars.referencedProfileId][vars.referencedContentId]
                .referenceCondition != address(0)
        ) {
            revert OspErrors.ContentNotPublic();
        }
        if (!_getGovernanceStorage()._tokenWhitelisted[vars.currency]) {
            revert OspErrors.InvalidToken();
        }
        megaphoneId = ++_getContentStorage()._megaphoneCount;
        address treasure = _getGovernanceStorage()._treasure;
        if (treasure == address(0)) revert OspErrors.InvalidTreasure();
        if (vars.currency == address(0)) {
            if (msg.value != vars.amount) revert OspErrors.DataMismatch();
            Payment.payNative(treasure, vars.amount);
        } else {
            Payment.payERC20(vars.currency, msg.sender, treasure, vars.amount);
        }
        emit OspEvents.MegaphoneCreated(
            megaphoneId,
            vars.referencedProfileId,
            vars.referencedContentId,
            vars.profileId,
            vars.tags,
            vars.startTime,
            vars.duration,
            vars.currency,
            vars.amount,
            vars.ctx,
            block.timestamp
        );
    }

    function _createOpenReaction(OspDataTypes.CreateOpenReactionData calldata vars) internal {
        _validateCallerIsJoinCommunity(vars.profileId, vars.communityId);
        if (msg.value < vars.reactionValue) {
            revert OspErrors.InvalidValue();
        }
        _validateReferenced(
            vars.profileId,
            vars.communityId,
            vars.referencedProfileId,
            vars.referencedContentId,
            vars.referenceConditionData,
            msg.value - vars.reactionValue
        );

        (address openReaction, bytes memory openReactionData) = _initOpenReaction(
            vars.profileId,
            vars.referencedProfileId,
            vars.referencedContentId,
            vars.reactionAndData,
            vars.reactionValue
        );

        emit OspEvents.OpenReactionCreated(
            vars.profileId,
            vars.referencedProfileId,
            vars.referencedContentId,
            vars.communityId,
            openReaction,
            openReactionData,
            vars.ctx,
            block.timestamp
        );
    }

    function _initReferenceCondition(
        uint256 profileId,
        uint256 contentId,
        uint256 communityId,
        bytes calldata initCode
    ) internal returns (address referenceCondition) {
        referenceCondition = address(bytes20(initCode[:20]));
        _checkReferenceCondition(referenceCondition);
        bytes memory initCallData = initCode[20:];
        IReferenceCondition(referenceCondition).initializeReferenceCondition(
            profileId,
            contentId,
            communityId,
            initCallData
        );
    }

    function _initActivityExtension(
        uint256 profileId,
        uint256 contentId,
        bytes calldata initCode,
        uint256 value
    ) internal returns (address extension) {
        extension = address(bytes20(initCode[:20]));
        _checkActivityExtension(extension);
        bytes memory initCallData = initCode[20:];
        IActivityExtension(extension).initializeActivityExtension{value: value}(
            profileId,
            contentId,
            initCallData
        );
    }

    function _initOpenReaction(
        uint256 profileId,
        uint256 referencedProfileId,
        uint256 referencedContentId,
        bytes calldata initCode,
        uint256 value
    ) internal returns (address openReaction, bytes memory openReactionData) {
        openReaction = address(bytes20(initCode[:20]));
        _checkOpenReaction(openReaction);
        openReactionData = initCode[20:];
        IOpenReaction(openReaction).processReaction{value: value}(
            profileId,
            referencedProfileId,
            referencedContentId,
            openReactionData
        );
    }

    function _validateReferenced(
        uint256 profileId,
        uint256 communityId,
        uint256 referencedProfileId,
        uint256 referencedContentId,
        bytes calldata referenceConditionData,
        uint256 value
    ) internal {
        OspDataTypes.ContentStruct storage referencedContent = _getContentStorage()
            ._contentByIdByProfile[referencedProfileId][referencedContentId];

        // Because contentId is a Hash in the offChainServer, not an increment.
        // We cannot use the contentCount of the Profile here to determine whether the article exists.
        if (
            bytes(referencedContent.contentURI).length != 0 ||
            referencedContent.referencedProfileId != 0
        ) {
            address referenceCondition = referencedContent.referenceCondition;
            if (referenceCondition != address(0)) {
                IReferenceCondition(referenceCondition).processReactionReference{value: value}(
                    profileId,
                    communityId,
                    referencedProfileId,
                    referencedContentId,
                    referenceConditionData
                );
            }
        } else {
            revert OspErrors.ContentDoesNotExist();
        }
    }

    function _validateCallerIsJoinCommunity(uint256 profileId, uint256 communityId) internal view {
        if (communityId != 0) {
            address owner = _ownerOf(profileId);
            address joinNFT = _getCommunityStorage()._communityById[communityId].joinNFT;
            if (joinNFT == address(0)) {
                revert OspErrors.InvalidCommunityId();
            }
            uint256 isJoin = IERC721(joinNFT).balanceOf(owner);
            if (isJoin == 0) {
                revert OspErrors.NotJoinCommunity();
            }
        }
    }

    function _validateActivity(uint256 communityId, address owner, uint256 profileId) internal {
        address activityCondition = _getCommunityStorage()
            ._communityById[communityId]
            .activityCondition;
        if (activityCondition == address(0)) return;
        IActivityCondition(activityCondition).processActivity(communityId, owner, profileId);
    }
}
合同源代码
文件 17 的 124:Context.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Context.sol)

pragma solidity ^0.8.20;

/**
 * @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;
    }
}
合同源代码
文件 18 的 124:ContextUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Context.sol)

pragma solidity ^0.8.20;
import {Initializable} from "../proxy/utils/Initializable.sol";

/**
 * @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 ContextUpgradeable is Initializable {
    function __Context_init() internal onlyInitializing {
    }

    function __Context_init_unchained() internal onlyInitializing {
    }
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}
合同源代码
文件 19 的 124:ContractMetadata.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;

/// @author thirdweb

import "../interface/IContractMetadata.sol";

/**
 *  @author  thirdweb.com
 *
 *  @title   Contract Metadata
 *  @notice  Thirdweb's `ContractMetadata` is a contract extension for any base contracts. It lets you set a metadata URI
 *           for you contract.
 *           Additionally, `ContractMetadata` is necessary for NFT contracts that want royalties to get distributed on OpenSea.
 */

library ContractMetadataStorage {
    /// @custom:storage-location erc7201:contract.metadata.storage
    /// @dev keccak256(abi.encode(uint256(keccak256("contract.metadata.storage")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 public constant CONTRACT_METADATA_STORAGE_POSITION =
        0x4bc804ba64359c0e35e5ed5d90ee596ecaa49a3a930ddcb1470ea0dd625da900;

    struct Data {
        /// @notice Returns the contract metadata URI.
        string contractURI;
    }

    function data() internal pure returns (Data storage data_) {
        bytes32 position = CONTRACT_METADATA_STORAGE_POSITION;
        assembly {
            data_.slot := position
        }
    }
}

abstract contract ContractMetadata is IContractMetadata {
    /**
     *  @notice         Lets a contract admin set the URI for contract-level metadata.
     *  @dev            Caller should be authorized to setup contractURI, e.g. contract admin.
     *                  See {_canSetContractURI}.
     *                  Emits {ContractURIUpdated Event}.
     *
     *  @param _uri     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
     */
    function setContractURI(string memory _uri) external override {
        if (!_canSetContractURI()) {
            revert("Not authorized");
        }

        _setupContractURI(_uri);
    }

    /// @dev Lets a contract admin set the URI for contract-level metadata.
    function _setupContractURI(string memory _uri) internal {
        string memory prevURI = _contractMetadataStorage().contractURI;
        _contractMetadataStorage().contractURI = _uri;

        emit ContractURIUpdated(prevURI, _uri);
    }

    /// @notice Returns the contract metadata URI.
    function contractURI() public view virtual override returns (string memory) {
        return _contractMetadataStorage().contractURI;
    }

    /// @dev Returns the AccountPermissions storage.
    function _contractMetadataStorage() internal pure returns (ContractMetadataStorage.Data storage data) {
        data = ContractMetadataStorage.data();
    }

    /// @dev Returns whether contract metadata can be set in the given execution context.
    function _canSetContractURI() internal view virtual returns (bool);
}
合同源代码
文件 20 的 124:ECDSA.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/ECDSA.sol)

pragma solidity ^0.8.20;

/**
 * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
 *
 * These functions can be used to verify that a message was signed by the holder
 * of the private keys of a given address.
 */
library ECDSA {
    enum RecoverError {
        NoError,
        InvalidSignature,
        InvalidSignatureLength,
        InvalidSignatureS
    }

    /**
     * @dev The signature derives the `address(0)`.
     */
    error ECDSAInvalidSignature();

    /**
     * @dev The signature has an invalid length.
     */
    error ECDSAInvalidSignatureLength(uint256 length);

    /**
     * @dev The signature has an S value that is in the upper half order.
     */
    error ECDSAInvalidSignatureS(bytes32 s);

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with `signature` or an error. This will not
     * return address(0) without also returning an error description. Errors are documented using an enum (error type)
     * and a bytes32 providing additional information about the error.
     *
     * If no error is returned, then the address can be used for verification purposes.
     *
     * The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
     *
     * Documentation for signature generation:
     * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
     * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
     */
    function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError, bytes32) {
        if (signature.length == 65) {
            bytes32 r;
            bytes32 s;
            uint8 v;
            // ecrecover takes the signature parameters, and the only way to get them
            // currently is to use assembly.
            /// @solidity memory-safe-assembly
            assembly {
                r := mload(add(signature, 0x20))
                s := mload(add(signature, 0x40))
                v := byte(0, mload(add(signature, 0x60)))
            }
            return tryRecover(hash, v, r, s);
        } else {
            return (address(0), RecoverError.InvalidSignatureLength, bytes32(signature.length));
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature`. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
     */
    function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
        (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, signature);
        _throwError(error, errorArg);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
     *
     * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
     */
    function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError, bytes32) {
        unchecked {
            bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
            // We do not check for an overflow here since the shift operation results in 0 or 1.
            uint8 v = uint8((uint256(vs) >> 255) + 27);
            return tryRecover(hash, v, r, s);
        }
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
     */
    function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
        (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, r, vs);
        _throwError(error, errorArg);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
     * `r` and `s` signature fields separately.
     */
    function tryRecover(
        bytes32 hash,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal pure returns (address, RecoverError, bytes32) {
        // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
        // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
        // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
        // signatures from current libraries generate a unique signature with an s-value in the lower half order.
        //
        // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
        // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
        // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
        // these malleable signatures as well.
        if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
            return (address(0), RecoverError.InvalidSignatureS, s);
        }

        // If the signature is valid (and not malleable), return the signer address
        address signer = ecrecover(hash, v, r, s);
        if (signer == address(0)) {
            return (address(0), RecoverError.InvalidSignature, bytes32(0));
        }

        return (signer, RecoverError.NoError, bytes32(0));
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `v`,
     * `r` and `s` signature fields separately.
     */
    function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
        (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, v, r, s);
        _throwError(error, errorArg);
        return recovered;
    }

    /**
     * @dev Optionally reverts with the corresponding custom error according to the `error` argument provided.
     */
    function _throwError(RecoverError error, bytes32 errorArg) private pure {
        if (error == RecoverError.NoError) {
            return; // no error: do nothing
        } else if (error == RecoverError.InvalidSignature) {
            revert ECDSAInvalidSignature();
        } else if (error == RecoverError.InvalidSignatureLength) {
            revert ECDSAInvalidSignatureLength(uint256(errorArg));
        } else if (error == RecoverError.InvalidSignatureS) {
            revert ECDSAInvalidSignatureS(errorArg);
        }
    }
}
合同源代码
文件 21 的 124:EIP712Base.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

import '../../libraries/OspDataTypes.sol';
import '../../libraries/OspErrors.sol';
import '@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol';

/**
 * @title EIP712Base
 * @author OpenSocial Protocol
 * @dev This contract is EIP712 implementation.
 * See https://eips.ethereum.org/EIPS/eip-712
 */
abstract contract EIP712Base {
    using SignatureChecker for address;
    bytes32 internal constant EIP712_REVISION_HASH = keccak256('1');

    /**
     * @dev Wrapper for ecrecover to reduce code size, used in meta-tx specific functions.
     */
    function _validateRecoveredAddress(
        bytes32 digest,
        OspDataTypes.EIP712Signature calldata sig
    ) internal view {
        if (sig.deadline < block.timestamp) revert OspErrors.SignatureExpired();
        if (!sig.signer.isValidSignatureNow(digest, sig.signature)) {
            revert OspErrors.SignatureInvalid();
        }
    }

    /**
     * @dev Calculates EIP712 DOMAIN_SEPARATOR based on the current contract and chain ID.
     */
    function _calculateDomainSeparator() internal view virtual returns (bytes32);

    /**
     * @dev Calculates EIP712 digest based on the current DOMAIN_SEPARATOR.
     *
     * @param hashedMessage The message hash from which the digest should be calculated.
     *
     * @return bytes32 A 32-byte output representing the EIP712 digest.
     */
    function _calculateDigest(bytes32 hashedMessage) internal view returns (bytes32) {
        bytes32 digest;
        unchecked {
            digest = keccak256(
                abi.encodePacked('\x19\x01', _calculateDomainSeparator(), hashedMessage)
            );
        }
        return digest;
    }
}
合同源代码
文件 22 的 124:ERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165.sol)

pragma solidity ^0.8.20;

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

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 */
abstract contract ERC165 is IERC165 {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}
合同源代码
文件 23 的 124:ERC165Checker.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165Checker.sol)

pragma solidity ^0.8.20;

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

/**
 * @dev Library used to query support of an interface declared via {IERC165}.
 *
 * Note that these functions return the actual result of the query: they do not
 * `revert` if an interface is not supported. It is up to the caller to decide
 * what to do in these cases.
 */
library ERC165Checker {
    // As per the EIP-165 spec, no interface should ever match 0xffffffff
    bytes4 private constant INTERFACE_ID_INVALID = 0xffffffff;

    /**
     * @dev Returns true if `account` supports the {IERC165} interface.
     */
    function supportsERC165(address account) internal view returns (bool) {
        // Any contract that implements ERC165 must explicitly indicate support of
        // InterfaceId_ERC165 and explicitly indicate non-support of InterfaceId_Invalid
        return
            supportsERC165InterfaceUnchecked(account, type(IERC165).interfaceId) &&
            !supportsERC165InterfaceUnchecked(account, INTERFACE_ID_INVALID);
    }

    /**
     * @dev Returns true if `account` supports the interface defined by
     * `interfaceId`. Support for {IERC165} itself is queried automatically.
     *
     * See {IERC165-supportsInterface}.
     */
    function supportsInterface(address account, bytes4 interfaceId) internal view returns (bool) {
        // query support of both ERC165 as per the spec and support of _interfaceId
        return supportsERC165(account) && supportsERC165InterfaceUnchecked(account, interfaceId);
    }

    /**
     * @dev Returns a boolean array where each value corresponds to the
     * interfaces passed in and whether they're supported or not. This allows
     * you to batch check interfaces for a contract where your expectation
     * is that some interfaces may not be supported.
     *
     * See {IERC165-supportsInterface}.
     */
    function getSupportedInterfaces(
        address account,
        bytes4[] memory interfaceIds
    ) internal view returns (bool[] memory) {
        // an array of booleans corresponding to interfaceIds and whether they're supported or not
        bool[] memory interfaceIdsSupported = new bool[](interfaceIds.length);

        // query support of ERC165 itself
        if (supportsERC165(account)) {
            // query support of each interface in interfaceIds
            for (uint256 i = 0; i < interfaceIds.length; i++) {
                interfaceIdsSupported[i] = supportsERC165InterfaceUnchecked(account, interfaceIds[i]);
            }
        }

        return interfaceIdsSupported;
    }

    /**
     * @dev Returns true if `account` supports all the interfaces defined in
     * `interfaceIds`. Support for {IERC165} itself is queried automatically.
     *
     * Batch-querying can lead to gas savings by skipping repeated checks for
     * {IERC165} support.
     *
     * See {IERC165-supportsInterface}.
     */
    function supportsAllInterfaces(address account, bytes4[] memory interfaceIds) internal view returns (bool) {
        // query support of ERC165 itself
        if (!supportsERC165(account)) {
            return false;
        }

        // query support of each interface in interfaceIds
        for (uint256 i = 0; i < interfaceIds.length; i++) {
            if (!supportsERC165InterfaceUnchecked(account, interfaceIds[i])) {
                return false;
            }
        }

        // all interfaces supported
        return true;
    }

    /**
     * @notice Query if a contract implements an interface, does not check ERC165 support
     * @param account The address of the contract to query for support of an interface
     * @param interfaceId The interface identifier, as specified in ERC-165
     * @return true if the contract at account indicates support of the interface with
     * identifier interfaceId, false otherwise
     * @dev Assumes that account contains a contract that supports ERC165, otherwise
     * the behavior of this method is undefined. This precondition can be checked
     * with {supportsERC165}.
     *
     * Some precompiled contracts will falsely indicate support for a given interface, so caution
     * should be exercised when using this function.
     *
     * Interface identification is specified in ERC-165.
     */
    function supportsERC165InterfaceUnchecked(address account, bytes4 interfaceId) internal view returns (bool) {
        // prepare call
        bytes memory encodedParams = abi.encodeCall(IERC165.supportsInterface, (interfaceId));

        // perform static call
        bool success;
        uint256 returnSize;
        uint256 returnValue;
        assembly {
            success := staticcall(30000, account, add(encodedParams, 0x20), mload(encodedParams), 0x00, 0x20)
            returnSize := returndatasize()
            returnValue := mload(0x00)
        }

        return success && returnSize >= 0x20 && returnValue > 0;
    }
}
合同源代码
文件 24 的 124:ERC165Upgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165.sol)

pragma solidity ^0.8.20;

import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {Initializable} from "../../proxy/utils/Initializable.sol";

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 */
abstract contract ERC165Upgradeable is Initializable, IERC165 {
    function __ERC165_init() internal onlyInitializing {
    }

    function __ERC165_init_unchained() internal onlyInitializing {
    }
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}
合同源代码
文件 25 的 124:ERC1967Proxy.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/ERC1967/ERC1967Proxy.sol)

pragma solidity ^0.8.20;

import {Proxy} from "../Proxy.sol";
import {ERC1967Utils} from "./ERC1967Utils.sol";

/**
 * @dev This contract implements an upgradeable proxy. It is upgradeable because calls are delegated to an
 * implementation address that can be changed. This address is stored in storage in the location specified by
 * https://eips.ethereum.org/EIPS/eip-1967[EIP1967], so that it doesn't conflict with the storage layout of the
 * implementation behind the proxy.
 */
contract ERC1967Proxy is Proxy {
    /**
     * @dev Initializes the upgradeable proxy with an initial implementation specified by `implementation`.
     *
     * If `_data` is nonempty, it's used as data in a delegate call to `implementation`. This will typically be an
     * encoded function call, and allows initializing the storage of the proxy like a Solidity constructor.
     *
     * Requirements:
     *
     * - If `data` is empty, `msg.value` must be zero.
     */
    constructor(address implementation, bytes memory _data) payable {
        ERC1967Utils.upgradeToAndCall(implementation, _data);
    }

    /**
     * @dev Returns the current implementation address.
     *
     * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using
     * the https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
     * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`
     */
    function _implementation() internal view virtual override returns (address) {
        return ERC1967Utils.getImplementation();
    }
}
合同源代码
文件 26 的 124:ERC1967Utils.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/ERC1967/ERC1967Utils.sol)

pragma solidity ^0.8.20;

import {IBeacon} from "../beacon/IBeacon.sol";
import {Address} from "../../utils/Address.sol";
import {StorageSlot} from "../../utils/StorageSlot.sol";

/**
 * @dev This abstract contract provides getters and event emitting update functions for
 * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
 */
library ERC1967Utils {
    // We re-declare ERC-1967 events here because they can't be used directly from IERC1967.
    // This will be fixed in Solidity 0.8.21. At that point we should remove these events.
    /**
     * @dev Emitted when the implementation is upgraded.
     */
    event Upgraded(address indexed implementation);

    /**
     * @dev Emitted when the admin account has changed.
     */
    event AdminChanged(address previousAdmin, address newAdmin);

    /**
     * @dev Emitted when the beacon is changed.
     */
    event BeaconUpgraded(address indexed beacon);

    /**
     * @dev Storage slot with the address of the current implementation.
     * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1.
     */
    // solhint-disable-next-line private-vars-leading-underscore
    bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;

    /**
     * @dev The `implementation` of the proxy is invalid.
     */
    error ERC1967InvalidImplementation(address implementation);

    /**
     * @dev The `admin` of the proxy is invalid.
     */
    error ERC1967InvalidAdmin(address admin);

    /**
     * @dev The `beacon` of the proxy is invalid.
     */
    error ERC1967InvalidBeacon(address beacon);

    /**
     * @dev An upgrade function sees `msg.value > 0` that may be lost.
     */
    error ERC1967NonPayable();

    /**
     * @dev Returns the current implementation address.
     */
    function getImplementation() internal view returns (address) {
        return StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value;
    }

    /**
     * @dev Stores a new address in the EIP1967 implementation slot.
     */
    function _setImplementation(address newImplementation) private {
        if (newImplementation.code.length == 0) {
            revert ERC1967InvalidImplementation(newImplementation);
        }
        StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value = newImplementation;
    }

    /**
     * @dev Performs implementation upgrade with additional setup call if data is nonempty.
     * This function is payable only if the setup call is performed, otherwise `msg.value` is rejected
     * to avoid stuck value in the contract.
     *
     * Emits an {IERC1967-Upgraded} event.
     */
    function upgradeToAndCall(address newImplementation, bytes memory data) internal {
        _setImplementation(newImplementation);
        emit Upgraded(newImplementation);

        if (data.length > 0) {
            Address.functionDelegateCall(newImplementation, data);
        } else {
            _checkNonPayable();
        }
    }

    /**
     * @dev Storage slot with the admin of the contract.
     * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1.
     */
    // solhint-disable-next-line private-vars-leading-underscore
    bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;

    /**
     * @dev Returns the current admin.
     *
     * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using
     * the https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
     * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
     */
    function getAdmin() internal view returns (address) {
        return StorageSlot.getAddressSlot(ADMIN_SLOT).value;
    }

    /**
     * @dev Stores a new address in the EIP1967 admin slot.
     */
    function _setAdmin(address newAdmin) private {
        if (newAdmin == address(0)) {
            revert ERC1967InvalidAdmin(address(0));
        }
        StorageSlot.getAddressSlot(ADMIN_SLOT).value = newAdmin;
    }

    /**
     * @dev Changes the admin of the proxy.
     *
     * Emits an {IERC1967-AdminChanged} event.
     */
    function changeAdmin(address newAdmin) internal {
        emit AdminChanged(getAdmin(), newAdmin);
        _setAdmin(newAdmin);
    }

    /**
     * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
     * This is the keccak-256 hash of "eip1967.proxy.beacon" subtracted by 1.
     */
    // solhint-disable-next-line private-vars-leading-underscore
    bytes32 internal constant BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;

    /**
     * @dev Returns the current beacon.
     */
    function getBeacon() internal view returns (address) {
        return StorageSlot.getAddressSlot(BEACON_SLOT).value;
    }

    /**
     * @dev Stores a new beacon in the EIP1967 beacon slot.
     */
    function _setBeacon(address newBeacon) private {
        if (newBeacon.code.length == 0) {
            revert ERC1967InvalidBeacon(newBeacon);
        }

        StorageSlot.getAddressSlot(BEACON_SLOT).value = newBeacon;

        address beaconImplementation = IBeacon(newBeacon).implementation();
        if (beaconImplementation.code.length == 0) {
            revert ERC1967InvalidImplementation(beaconImplementation);
        }
    }

    /**
     * @dev Change the beacon and trigger a setup call if data is nonempty.
     * This function is payable only if the setup call is performed, otherwise `msg.value` is rejected
     * to avoid stuck value in the contract.
     *
     * Emits an {IERC1967-BeaconUpgraded} event.
     *
     * CAUTION: Invoking this function has no effect on an instance of {BeaconProxy} since v5, since
     * it uses an immutable beacon without looking at the value of the ERC-1967 beacon slot for
     * efficiency.
     */
    function upgradeBeaconToAndCall(address newBeacon, bytes memory data) internal {
        _setBeacon(newBeacon);
        emit BeaconUpgraded(newBeacon);

        if (data.length > 0) {
            Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
        } else {
            _checkNonPayable();
        }
    }

    /**
     * @dev Reverts if `msg.value` is not zero. It can be used to avoid `msg.value` stuck in the contract
     * if an upgrade doesn't perform an initialization call.
     */
    function _checkNonPayable() private {
        if (msg.value > 0) {
            revert ERC1967NonPayable();
        }
    }
}
合同源代码
文件 27 的 124:ERC20FeeJoinCond.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

import {OspErrors} from '../../../libraries/OspErrors.sol';
import {FeeConditionBase} from '../FeeConditionBase.sol';
import {JoinConditionBase} from '../../base/JoinConditionBase.sol';
import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import {SafeERC20} from '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';

/**
 * @param currency The address of the ERC20 token to be used for the fee.
 * @param amount The amount of the ERC20 token to be used for the fee.
 * @param recipient The address of the recipient of the fee.
 */
struct CommunityData {
    address currency;
    uint256 amount;
    address recipient;
}

/**
 * @title ERC20FeeJoinCond
 * @author OpenSocial
 * @notice The condition allows users to join a community by paying a fee in ERC20 tokens.
 */
contract ERC20FeeJoinCond is FeeConditionBase, JoinConditionBase {
    using SafeERC20 for IERC20;

    mapping(uint256 => CommunityData) internal _dataByCommunity;

    constructor(address osp) JoinConditionBase(osp) {}

    /**
     * @dev Initializes the condition data for a given community.
     */
    function _initializeCommunityJoinCondition(
        uint256 communityId,
        bytes calldata data
    ) internal override {
        (address currency, uint256 amount, address recipient) = abi.decode(
            data,
            (address, uint256, address)
        );
        if (!_tokenWhitelisted(currency) || recipient == address(0) || amount == 0)
            revert OspErrors.InitParamsInvalid();
        _dataByCommunity[communityId].amount = amount;
        _dataByCommunity[communityId].currency = currency;
        _dataByCommunity[communityId].recipient = recipient;
    }

    /**
     * @dev process join,transfer ERC20 token to recipient.
     */
    function _processJoin(
        address follower,
        uint256 communityId,
        bytes calldata data
    ) internal override nonPayable {
        uint256 amount = _dataByCommunity[communityId].amount;
        address currency = _dataByCommunity[communityId].currency;
        _validateDataIsExpected(data, currency, amount);
        IERC20(currency).safeTransferFrom(
            follower,
            _dataByCommunity[communityId].recipient,
            amount
        );
    }

    /**
     * @dev Returns the community data for a given community, or an empty struct if that community was not initialized
     * with this condition.
     */
    function getCommunityData(uint256 communityId) external view returns (CommunityData memory) {
        return _dataByCommunity[communityId];
    }
}
合同源代码
文件 28 的 124:ERC2981Upgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/common/ERC2981.sol)

pragma solidity ^0.8.20;

import {IERC2981} from "@openzeppelin/contracts/interfaces/IERC2981.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {ERC165Upgradeable} from "../../utils/introspection/ERC165Upgradeable.sol";
import {Initializable} from "../../proxy/utils/Initializable.sol";

/**
 * @dev Implementation of the NFT Royalty Standard, a standardized way to retrieve royalty payment information.
 *
 * Royalty information can be specified globally for all token ids via {_setDefaultRoyalty}, and/or individually for
 * specific token ids via {_setTokenRoyalty}. The latter takes precedence over the first.
 *
 * Royalty is specified as a fraction of sale price. {_feeDenominator} is overridable but defaults to 10000, meaning the
 * fee is specified in basis points by default.
 *
 * IMPORTANT: ERC-2981 only specifies a way to signal royalty information and does not enforce its payment. See
 * https://eips.ethereum.org/EIPS/eip-2981#optional-royalty-payments[Rationale] in the EIP. Marketplaces are expected to
 * voluntarily pay royalties together with sales, but note that this standard is not yet widely supported.
 */
abstract contract ERC2981Upgradeable is Initializable, IERC2981, ERC165Upgradeable {
    struct RoyaltyInfo {
        address receiver;
        uint96 royaltyFraction;
    }

    /// @custom:storage-location erc7201:openzeppelin.storage.ERC2981
    struct ERC2981Storage {
        RoyaltyInfo _defaultRoyaltyInfo;
        mapping(uint256 tokenId => RoyaltyInfo) _tokenRoyaltyInfo;
    }

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ERC2981")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant ERC2981StorageLocation = 0xdaedc9ab023613a7caf35e703657e986ccfad7e3eb0af93a2853f8d65dd86b00;

    function _getERC2981Storage() private pure returns (ERC2981Storage storage $) {
        assembly {
            $.slot := ERC2981StorageLocation
        }
    }

    /**
     * @dev The default royalty set is invalid (eg. (numerator / denominator) >= 1).
     */
    error ERC2981InvalidDefaultRoyalty(uint256 numerator, uint256 denominator);

    /**
     * @dev The default royalty receiver is invalid.
     */
    error ERC2981InvalidDefaultRoyaltyReceiver(address receiver);

    /**
     * @dev The royalty set for an specific `tokenId` is invalid (eg. (numerator / denominator) >= 1).
     */
    error ERC2981InvalidTokenRoyalty(uint256 tokenId, uint256 numerator, uint256 denominator);

    /**
     * @dev The royalty receiver for `tokenId` is invalid.
     */
    error ERC2981InvalidTokenRoyaltyReceiver(uint256 tokenId, address receiver);

    function __ERC2981_init() internal onlyInitializing {
    }

    function __ERC2981_init_unchained() internal onlyInitializing {
    }
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC165Upgradeable) returns (bool) {
        return interfaceId == type(IERC2981).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @inheritdoc IERC2981
     */
    function royaltyInfo(uint256 tokenId, uint256 salePrice) public view virtual returns (address, uint256) {
        ERC2981Storage storage $ = _getERC2981Storage();
        RoyaltyInfo memory royalty = $._tokenRoyaltyInfo[tokenId];

        if (royalty.receiver == address(0)) {
            royalty = $._defaultRoyaltyInfo;
        }

        uint256 royaltyAmount = (salePrice * royalty.royaltyFraction) / _feeDenominator();

        return (royalty.receiver, royaltyAmount);
    }

    /**
     * @dev The denominator with which to interpret the fee set in {_setTokenRoyalty} and {_setDefaultRoyalty} as a
     * fraction of the sale price. Defaults to 10000 so fees are expressed in basis points, but may be customized by an
     * override.
     */
    function _feeDenominator() internal pure virtual returns (uint96) {
        return 10000;
    }

    /**
     * @dev Sets the royalty information that all ids in this contract will default to.
     *
     * Requirements:
     *
     * - `receiver` cannot be the zero address.
     * - `feeNumerator` cannot be greater than the fee denominator.
     */
    function _setDefaultRoyalty(address receiver, uint96 feeNumerator) internal virtual {
        ERC2981Storage storage $ = _getERC2981Storage();
        uint256 denominator = _feeDenominator();
        if (feeNumerator > denominator) {
            // Royalty fee will exceed the sale price
            revert ERC2981InvalidDefaultRoyalty(feeNumerator, denominator);
        }
        if (receiver == address(0)) {
            revert ERC2981InvalidDefaultRoyaltyReceiver(address(0));
        }

        $._defaultRoyaltyInfo = RoyaltyInfo(receiver, feeNumerator);
    }

    /**
     * @dev Removes default royalty information.
     */
    function _deleteDefaultRoyalty() internal virtual {
        ERC2981Storage storage $ = _getERC2981Storage();
        delete $._defaultRoyaltyInfo;
    }

    /**
     * @dev Sets the royalty information for a specific token id, overriding the global default.
     *
     * Requirements:
     *
     * - `receiver` cannot be the zero address.
     * - `feeNumerator` cannot be greater than the fee denominator.
     */
    function _setTokenRoyalty(uint256 tokenId, address receiver, uint96 feeNumerator) internal virtual {
        ERC2981Storage storage $ = _getERC2981Storage();
        uint256 denominator = _feeDenominator();
        if (feeNumerator > denominator) {
            // Royalty fee will exceed the sale price
            revert ERC2981InvalidTokenRoyalty(tokenId, feeNumerator, denominator);
        }
        if (receiver == address(0)) {
            revert ERC2981InvalidTokenRoyaltyReceiver(tokenId, address(0));
        }

        $._tokenRoyaltyInfo[tokenId] = RoyaltyInfo(receiver, feeNumerator);
    }

    /**
     * @dev Resets royalty information for the token id back to the global default.
     */
    function _resetTokenRoyalty(uint256 tokenId) internal virtual {
        ERC2981Storage storage $ = _getERC2981Storage();
        delete $._tokenRoyaltyInfo[tokenId];
    }
}
合同源代码
文件 29 的 124:ERC6551Account.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

/* solhint-disable no-empty-blocks */

import {IERC165} from '@openzeppelin/contracts/utils/introspection/IERC165.sol';
import {IERC721Receiver} from '@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol';
import {IERC1155Receiver} from '@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol';
import {OspErrors} from '../../libraries/OspErrors.sol';
import {OspClient} from '../logics/interfaces/OspClient.sol';
import {IERC721} from '@openzeppelin/contracts/token/ERC721/IERC721.sol';

contract ERC6551Account is IERC721Receiver, IERC1155Receiver {
    struct Execution {
        // The target contract for account to execute.
        address target;
        // The value for the execution.
        uint256 value;
        // The call data for the execution.
        bytes data;
    }

    address immutable OSP;

    uint256 public tokenId;

    constructor(address osp) {
        OSP = osp;
    }

    /**
     * @dev initialize communityId, only callable by OSP
     */
    function initialize(uint256 communityId) external {
        if (msg.sender != OSP) revert OspErrors.NotOSP();
        tokenId = communityId;
    }

    /**
     * @dev For previous TBA accounts,initialize communityId.
     */
    function setCommunityId(uint256 communityId) external {
        if (tokenId == 0 && OspClient(OSP).getCommunityAccount(communityId) == address(this)) {
            tokenId = communityId;
            return;
        }
        revert OspErrors.InitParamsInvalid();
    }

    /// @notice Executes a transaction from the account
    /// @param execution The execution to perform
    /// @return result The result of the execution
    function execute(
        Execution calldata execution
    ) public payable virtual returns (bytes memory result) {
        _onlyOwner(msg.sender);
        result = _exec(execution.target, execution.value, execution.data);
    }

    /// @notice Executes a batch of transactions from the account
    /// @dev If any of the transactions revert, the entire batch reverts
    /// @param executions The executions to perform
    /// @return results The results of the executions
    function executeBatch(
        Execution[] calldata executions
    ) public payable virtual returns (bytes[] memory results) {
        _onlyOwner(msg.sender);
        uint256 executionsLength = executions.length;
        results = new bytes[](executionsLength);

        for (uint256 i = 0; i < executionsLength; ) {
            results[i] = _exec(executions[i].target, executions[i].value, executions[i].data);
            unchecked {
                ++i;
            }
        }
    }

    function onERC721Received(
        address,
        address,
        uint256,
        bytes calldata
    ) external pure override returns (bytes4) {
        return IERC721Receiver.onERC721Received.selector;
    }

    function onERC1155Received(
        address,
        address,
        uint256,
        uint256,
        bytes calldata
    ) external pure override returns (bytes4) {
        return IERC1155Receiver.onERC1155Received.selector;
    }

    function onERC1155BatchReceived(
        address,
        address,
        uint256[] calldata,
        uint256[] calldata,
        bytes calldata
    ) external pure override returns (bytes4) {
        return IERC1155Receiver.onERC1155BatchReceived.selector;
    }

    function supportsInterface(bytes4 interfaceId) external view virtual override returns (bool) {
        return
            interfaceId == type(IERC721Receiver).interfaceId ||
            interfaceId == type(IERC1155Receiver).interfaceId ||
            interfaceId == type(IERC165).interfaceId;
    }

    function _exec(
        address target,
        uint256 value,
        bytes memory data
    ) internal returns (bytes memory result) {
        bool success;
        (success, result) = target.call{value: value}(data);

        if (!success) {
            // Directly bubble up revert messages
            assembly ('memory-safe') {
                revert(add(result, 32), mload(result))
            }
        }
    }

    function _onlyOwner(address account) internal view {
        if (account != IERC721(OspClient(OSP).getCommunityNFT()).ownerOf(tokenId))
            revert OspErrors.NotCommunityOwner();
    }

    // solhint-disable-next-line no-empty-blocks
    receive() external payable {}
}
合同源代码
文件 30 的 124:ERC721BurnableUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/extensions/ERC721Burnable.sol)

pragma solidity ^0.8.20;

import {ERC721Upgradeable} from "../ERC721Upgradeable.sol";
import {ContextUpgradeable} from "../../../utils/ContextUpgradeable.sol";
import {Initializable} from "../../../proxy/utils/Initializable.sol";

/**
 * @title ERC721 Burnable Token
 * @dev ERC721 Token that can be burned (destroyed).
 */
abstract contract ERC721BurnableUpgradeable is Initializable, ContextUpgradeable, ERC721Upgradeable {
    function __ERC721Burnable_init() internal onlyInitializing {
    }

    function __ERC721Burnable_init_unchained() internal onlyInitializing {
    }
    /**
     * @dev Burns `tokenId`. See {ERC721-_burn}.
     *
     * Requirements:
     *
     * - The caller must own `tokenId` or be an approved operator.
     */
    function burn(uint256 tokenId) public virtual {
        // Setting an "auth" arguments enables the `_isAuthorized` check which verifies that the token exists
        // (from != 0). Therefore, it is not needed to verify that the return value is not 0 here.
        _update(address(0), tokenId, _msgSender());
    }
}
合同源代码
文件 31 的 124:ERC721EnumerableUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/extensions/ERC721Enumerable.sol)

pragma solidity ^0.8.20;

import {ERC721Upgradeable} from "../ERC721Upgradeable.sol";
import {IERC721Enumerable} from "@openzeppelin/contracts/token/ERC721/extensions/IERC721Enumerable.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {Initializable} from "../../../proxy/utils/Initializable.sol";

/**
 * @dev This implements an optional extension of {ERC721} defined in the EIP that adds enumerability
 * of all the token ids in the contract as well as all token ids owned by each account.
 *
 * CAUTION: `ERC721` extensions that implement custom `balanceOf` logic, such as `ERC721Consecutive`,
 * interfere with enumerability and should not be used together with `ERC721Enumerable`.
 */
abstract contract ERC721EnumerableUpgradeable is Initializable, ERC721Upgradeable, IERC721Enumerable {
    /// @custom:storage-location erc7201:openzeppelin.storage.ERC721Enumerable
    struct ERC721EnumerableStorage {
        mapping(address owner => mapping(uint256 index => uint256)) _ownedTokens;
        mapping(uint256 tokenId => uint256) _ownedTokensIndex;

        uint256[] _allTokens;
        mapping(uint256 tokenId => uint256) _allTokensIndex;
    }

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ERC721Enumerable")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant ERC721EnumerableStorageLocation = 0x645e039705490088daad89bae25049a34f4a9072d398537b1ab2425f24cbed00;

    function _getERC721EnumerableStorage() private pure returns (ERC721EnumerableStorage storage $) {
        assembly {
            $.slot := ERC721EnumerableStorageLocation
        }
    }

    /**
     * @dev An `owner`'s token query was out of bounds for `index`.
     *
     * NOTE: The owner being `address(0)` indicates a global out of bounds index.
     */
    error ERC721OutOfBoundsIndex(address owner, uint256 index);

    /**
     * @dev Batch mint is not allowed.
     */
    error ERC721EnumerableForbiddenBatchMint();

    function __ERC721Enumerable_init() internal onlyInitializing {
    }

    function __ERC721Enumerable_init_unchained() internal onlyInitializing {
    }
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC721Upgradeable) returns (bool) {
        return interfaceId == type(IERC721Enumerable).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @dev See {IERC721Enumerable-tokenOfOwnerByIndex}.
     */
    function tokenOfOwnerByIndex(address owner, uint256 index) public view virtual returns (uint256) {
        ERC721EnumerableStorage storage $ = _getERC721EnumerableStorage();
        if (index >= balanceOf(owner)) {
            revert ERC721OutOfBoundsIndex(owner, index);
        }
        return $._ownedTokens[owner][index];
    }

    /**
     * @dev See {IERC721Enumerable-totalSupply}.
     */
    function totalSupply() public view virtual returns (uint256) {
        ERC721EnumerableStorage storage $ = _getERC721EnumerableStorage();
        return $._allTokens.length;
    }

    /**
     * @dev See {IERC721Enumerable-tokenByIndex}.
     */
    function tokenByIndex(uint256 index) public view virtual returns (uint256) {
        ERC721EnumerableStorage storage $ = _getERC721EnumerableStorage();
        if (index >= totalSupply()) {
            revert ERC721OutOfBoundsIndex(address(0), index);
        }
        return $._allTokens[index];
    }

    /**
     * @dev See {ERC721-_update}.
     */
    function _update(address to, uint256 tokenId, address auth) internal virtual override returns (address) {
        address previousOwner = super._update(to, tokenId, auth);

        if (previousOwner == address(0)) {
            _addTokenToAllTokensEnumeration(tokenId);
        } else if (previousOwner != to) {
            _removeTokenFromOwnerEnumeration(previousOwner, tokenId);
        }
        if (to == address(0)) {
            _removeTokenFromAllTokensEnumeration(tokenId);
        } else if (previousOwner != to) {
            _addTokenToOwnerEnumeration(to, tokenId);
        }

        return previousOwner;
    }

    /**
     * @dev Private function to add a token to this extension's ownership-tracking data structures.
     * @param to address representing the new owner of the given token ID
     * @param tokenId uint256 ID of the token to be added to the tokens list of the given address
     */
    function _addTokenToOwnerEnumeration(address to, uint256 tokenId) private {
        ERC721EnumerableStorage storage $ = _getERC721EnumerableStorage();
        uint256 length = balanceOf(to) - 1;
        $._ownedTokens[to][length] = tokenId;
        $._ownedTokensIndex[tokenId] = length;
    }

    /**
     * @dev Private function to add a token to this extension's token tracking data structures.
     * @param tokenId uint256 ID of the token to be added to the tokens list
     */
    function _addTokenToAllTokensEnumeration(uint256 tokenId) private {
        ERC721EnumerableStorage storage $ = _getERC721EnumerableStorage();
        $._allTokensIndex[tokenId] = $._allTokens.length;
        $._allTokens.push(tokenId);
    }

    /**
     * @dev Private function to remove a token from this extension's ownership-tracking data structures. Note that
     * while the token is not assigned a new owner, the `_ownedTokensIndex` mapping is _not_ updated: this allows for
     * gas optimizations e.g. when performing a transfer operation (avoiding double writes).
     * This has O(1) time complexity, but alters the order of the _ownedTokens array.
     * @param from address representing the previous owner of the given token ID
     * @param tokenId uint256 ID of the token to be removed from the tokens list of the given address
     */
    function _removeTokenFromOwnerEnumeration(address from, uint256 tokenId) private {
        ERC721EnumerableStorage storage $ = _getERC721EnumerableStorage();
        // To prevent a gap in from's tokens array, we store the last token in the index of the token to delete, and
        // then delete the last slot (swap and pop).

        uint256 lastTokenIndex = balanceOf(from);
        uint256 tokenIndex = $._ownedTokensIndex[tokenId];

        // When the token to delete is the last token, the swap operation is unnecessary
        if (tokenIndex != lastTokenIndex) {
            uint256 lastTokenId = $._ownedTokens[from][lastTokenIndex];

            $._ownedTokens[from][tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
            $._ownedTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index
        }

        // This also deletes the contents at the last position of the array
        delete $._ownedTokensIndex[tokenId];
        delete $._ownedTokens[from][lastTokenIndex];
    }

    /**
     * @dev Private function to remove a token from this extension's token tracking data structures.
     * This has O(1) time complexity, but alters the order of the _allTokens array.
     * @param tokenId uint256 ID of the token to be removed from the tokens list
     */
    function _removeTokenFromAllTokensEnumeration(uint256 tokenId) private {
        ERC721EnumerableStorage storage $ = _getERC721EnumerableStorage();
        // To prevent a gap in the tokens array, we store the last token in the index of the token to delete, and
        // then delete the last slot (swap and pop).

        uint256 lastTokenIndex = $._allTokens.length - 1;
        uint256 tokenIndex = $._allTokensIndex[tokenId];

        // When the token to delete is the last token, the swap operation is unnecessary. However, since this occurs so
        // rarely (when the last minted token is burnt) that we still do the swap here to avoid the gas cost of adding
        // an 'if' statement (like in _removeTokenFromOwnerEnumeration)
        uint256 lastTokenId = $._allTokens[lastTokenIndex];

        $._allTokens[tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
        $._allTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index

        // This also deletes the contents at the last position of the array
        delete $._allTokensIndex[tokenId];
        $._allTokens.pop();
    }

    /**
     * See {ERC721-_increaseBalance}. We need that to account tokens that were minted in batch
     */
    function _increaseBalance(address account, uint128 amount) internal virtual override {
        if (amount > 0) {
            revert ERC721EnumerableForbiddenBatchMint();
        }
        super._increaseBalance(account, amount);
    }
}
合同源代码
文件 32 的 124:ERC721Upgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/ERC721.sol)

pragma solidity ^0.8.20;

import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import {IERC721Metadata} from "@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol";
import {ContextUpgradeable} from "../../utils/ContextUpgradeable.sol";
import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {ERC165Upgradeable} from "../../utils/introspection/ERC165Upgradeable.sol";
import {IERC721Errors} from "@openzeppelin/contracts/interfaces/draft-IERC6093.sol";
import {Initializable} from "../../proxy/utils/Initializable.sol";

/**
 * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including
 * the Metadata extension, but not including the Enumerable extension, which is available separately as
 * {ERC721Enumerable}.
 */
abstract contract ERC721Upgradeable is Initializable, ContextUpgradeable, ERC165Upgradeable, IERC721, IERC721Metadata, IERC721Errors {
    using Strings for uint256;

    /// @custom:storage-location erc7201:openzeppelin.storage.ERC721
    struct ERC721Storage {
        // Token name
        string _name;

        // Token symbol
        string _symbol;

        mapping(uint256 tokenId => address) _owners;

        mapping(address owner => uint256) _balances;

        mapping(uint256 tokenId => address) _tokenApprovals;

        mapping(address owner => mapping(address operator => bool)) _operatorApprovals;
    }

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ERC721")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant ERC721StorageLocation = 0x80bb2b638cc20bc4d0a60d66940f3ab4a00c1d7b313497ca82fb0b4ab0079300;

    function _getERC721Storage() private pure returns (ERC721Storage storage $) {
        assembly {
            $.slot := ERC721StorageLocation
        }
    }

    /**
     * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
     */
    function __ERC721_init(string memory name_, string memory symbol_) internal onlyInitializing {
        __ERC721_init_unchained(name_, symbol_);
    }

    function __ERC721_init_unchained(string memory name_, string memory symbol_) internal onlyInitializing {
        ERC721Storage storage $ = _getERC721Storage();
        $._name = name_;
        $._symbol = symbol_;
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165Upgradeable, IERC165) returns (bool) {
        return
            interfaceId == type(IERC721).interfaceId ||
            interfaceId == type(IERC721Metadata).interfaceId ||
            super.supportsInterface(interfaceId);
    }

    /**
     * @dev See {IERC721-balanceOf}.
     */
    function balanceOf(address owner) public view virtual returns (uint256) {
        ERC721Storage storage $ = _getERC721Storage();
        if (owner == address(0)) {
            revert ERC721InvalidOwner(address(0));
        }
        return $._balances[owner];
    }

    /**
     * @dev See {IERC721-ownerOf}.
     */
    function ownerOf(uint256 tokenId) public view virtual returns (address) {
        return _requireOwned(tokenId);
    }

    /**
     * @dev See {IERC721Metadata-name}.
     */
    function name() public view virtual returns (string memory) {
        ERC721Storage storage $ = _getERC721Storage();
        return $._name;
    }

    /**
     * @dev See {IERC721Metadata-symbol}.
     */
    function symbol() public view virtual returns (string memory) {
        ERC721Storage storage $ = _getERC721Storage();
        return $._symbol;
    }

    /**
     * @dev See {IERC721Metadata-tokenURI}.
     */
    function tokenURI(uint256 tokenId) public view virtual returns (string memory) {
        _requireOwned(tokenId);

        string memory baseURI = _baseURI();
        return bytes(baseURI).length > 0 ? string.concat(baseURI, tokenId.toString()) : "";
    }

    /**
     * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
     * token will be the concatenation of the `baseURI` and the `tokenId`. Empty
     * by default, can be overridden in child contracts.
     */
    function _baseURI() internal view virtual returns (string memory) {
        return "";
    }

    /**
     * @dev See {IERC721-approve}.
     */
    function approve(address to, uint256 tokenId) public virtual {
        _approve(to, tokenId, _msgSender());
    }

    /**
     * @dev See {IERC721-getApproved}.
     */
    function getApproved(uint256 tokenId) public view virtual returns (address) {
        _requireOwned(tokenId);

        return _getApproved(tokenId);
    }

    /**
     * @dev See {IERC721-setApprovalForAll}.
     */
    function setApprovalForAll(address operator, bool approved) public virtual {
        _setApprovalForAll(_msgSender(), operator, approved);
    }

    /**
     * @dev See {IERC721-isApprovedForAll}.
     */
    function isApprovedForAll(address owner, address operator) public view virtual returns (bool) {
        ERC721Storage storage $ = _getERC721Storage();
        return $._operatorApprovals[owner][operator];
    }

    /**
     * @dev See {IERC721-transferFrom}.
     */
    function transferFrom(address from, address to, uint256 tokenId) public virtual {
        if (to == address(0)) {
            revert ERC721InvalidReceiver(address(0));
        }
        // Setting an "auth" arguments enables the `_isAuthorized` check which verifies that the token exists
        // (from != 0). Therefore, it is not needed to verify that the return value is not 0 here.
        address previousOwner = _update(to, tokenId, _msgSender());
        if (previousOwner != from) {
            revert ERC721IncorrectOwner(from, tokenId, previousOwner);
        }
    }

    /**
     * @dev See {IERC721-safeTransferFrom}.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId) public {
        safeTransferFrom(from, to, tokenId, "");
    }

    /**
     * @dev See {IERC721-safeTransferFrom}.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public virtual {
        transferFrom(from, to, tokenId);
        _checkOnERC721Received(from, to, tokenId, data);
    }

    /**
     * @dev Returns the owner of the `tokenId`. Does NOT revert if token doesn't exist
     *
     * IMPORTANT: Any overrides to this function that add ownership of tokens not tracked by the
     * core ERC721 logic MUST be matched with the use of {_increaseBalance} to keep balances
     * consistent with ownership. The invariant to preserve is that for any address `a` the value returned by
     * `balanceOf(a)` must be equal to the number of tokens such that `_ownerOf(tokenId)` is `a`.
     */
    function _ownerOf(uint256 tokenId) internal view virtual returns (address) {
        ERC721Storage storage $ = _getERC721Storage();
        return $._owners[tokenId];
    }

    /**
     * @dev Returns the approved address for `tokenId`. Returns 0 if `tokenId` is not minted.
     */
    function _getApproved(uint256 tokenId) internal view virtual returns (address) {
        ERC721Storage storage $ = _getERC721Storage();
        return $._tokenApprovals[tokenId];
    }

    /**
     * @dev Returns whether `spender` is allowed to manage `owner`'s tokens, or `tokenId` in
     * particular (ignoring whether it is owned by `owner`).
     *
     * WARNING: This function assumes that `owner` is the actual owner of `tokenId` and does not verify this
     * assumption.
     */
    function _isAuthorized(address owner, address spender, uint256 tokenId) internal view virtual returns (bool) {
        return
            spender != address(0) &&
            (owner == spender || isApprovedForAll(owner, spender) || _getApproved(tokenId) == spender);
    }

    /**
     * @dev Checks if `spender` can operate on `tokenId`, assuming the provided `owner` is the actual owner.
     * Reverts if `spender` does not have approval from the provided `owner` for the given token or for all its assets
     * the `spender` for the specific `tokenId`.
     *
     * WARNING: This function assumes that `owner` is the actual owner of `tokenId` and does not verify this
     * assumption.
     */
    function _checkAuthorized(address owner, address spender, uint256 tokenId) internal view virtual {
        if (!_isAuthorized(owner, spender, tokenId)) {
            if (owner == address(0)) {
                revert ERC721NonexistentToken(tokenId);
            } else {
                revert ERC721InsufficientApproval(spender, tokenId);
            }
        }
    }

    /**
     * @dev Unsafe write access to the balances, used by extensions that "mint" tokens using an {ownerOf} override.
     *
     * NOTE: the value is limited to type(uint128).max. This protect against _balance overflow. It is unrealistic that
     * a uint256 would ever overflow from increments when these increments are bounded to uint128 values.
     *
     * WARNING: Increasing an account's balance using this function tends to be paired with an override of the
     * {_ownerOf} function to resolve the ownership of the corresponding tokens so that balances and ownership
     * remain consistent with one another.
     */
    function _increaseBalance(address account, uint128 value) internal virtual {
        ERC721Storage storage $ = _getERC721Storage();
        unchecked {
            $._balances[account] += value;
        }
    }

    /**
     * @dev Transfers `tokenId` from its current owner to `to`, or alternatively mints (or burns) if the current owner
     * (or `to`) is the zero address. Returns the owner of the `tokenId` before the update.
     *
     * The `auth` argument is optional. If the value passed is non 0, then this function will check that
     * `auth` is either the owner of the token, or approved to operate on the token (by the owner).
     *
     * Emits a {Transfer} event.
     *
     * NOTE: If overriding this function in a way that tracks balances, see also {_increaseBalance}.
     */
    function _update(address to, uint256 tokenId, address auth) internal virtual returns (address) {
        ERC721Storage storage $ = _getERC721Storage();
        address from = _ownerOf(tokenId);

        // Perform (optional) operator check
        if (auth != address(0)) {
            _checkAuthorized(from, auth, tokenId);
        }

        // Execute the update
        if (from != address(0)) {
            // Clear approval. No need to re-authorize or emit the Approval event
            _approve(address(0), tokenId, address(0), false);

            unchecked {
                $._balances[from] -= 1;
            }
        }

        if (to != address(0)) {
            unchecked {
                $._balances[to] += 1;
            }
        }

        $._owners[tokenId] = to;

        emit Transfer(from, to, tokenId);

        return from;
    }

    /**
     * @dev Mints `tokenId` and transfers it to `to`.
     *
     * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible
     *
     * Requirements:
     *
     * - `tokenId` must not exist.
     * - `to` cannot be the zero address.
     *
     * Emits a {Transfer} event.
     */
    function _mint(address to, uint256 tokenId) internal {
        if (to == address(0)) {
            revert ERC721InvalidReceiver(address(0));
        }
        address previousOwner = _update(to, tokenId, address(0));
        if (previousOwner != address(0)) {
            revert ERC721InvalidSender(address(0));
        }
    }

    /**
     * @dev Mints `tokenId`, transfers it to `to` and checks for `to` acceptance.
     *
     * Requirements:
     *
     * - `tokenId` must not exist.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function _safeMint(address to, uint256 tokenId) internal {
        _safeMint(to, tokenId, "");
    }

    /**
     * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is
     * forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
     */
    function _safeMint(address to, uint256 tokenId, bytes memory data) internal virtual {
        _mint(to, tokenId);
        _checkOnERC721Received(address(0), to, tokenId, data);
    }

    /**
     * @dev Destroys `tokenId`.
     * The approval is cleared when the token is burned.
     * This is an internal function that does not check if the sender is authorized to operate on the token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     *
     * Emits a {Transfer} event.
     */
    function _burn(uint256 tokenId) internal {
        address previousOwner = _update(address(0), tokenId, address(0));
        if (previousOwner == address(0)) {
            revert ERC721NonexistentToken(tokenId);
        }
    }

    /**
     * @dev Transfers `tokenId` from `from` to `to`.
     *  As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     *
     * Emits a {Transfer} event.
     */
    function _transfer(address from, address to, uint256 tokenId) internal {
        if (to == address(0)) {
            revert ERC721InvalidReceiver(address(0));
        }
        address previousOwner = _update(to, tokenId, address(0));
        if (previousOwner == address(0)) {
            revert ERC721NonexistentToken(tokenId);
        } else if (previousOwner != from) {
            revert ERC721IncorrectOwner(from, tokenId, previousOwner);
        }
    }

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking that contract recipients
     * are aware of the ERC721 standard to prevent tokens from being forever locked.
     *
     * `data` is additional data, it has no specified format and it is sent in call to `to`.
     *
     * This internal function is like {safeTransferFrom} in the sense that it invokes
     * {IERC721Receiver-onERC721Received} on the receiver, and can be used to e.g.
     * implement alternative mechanisms to perform token transfer, such as signature-based.
     *
     * Requirements:
     *
     * - `tokenId` token must exist and be owned by `from`.
     * - `to` cannot be the zero address.
     * - `from` cannot be the zero address.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function _safeTransfer(address from, address to, uint256 tokenId) internal {
        _safeTransfer(from, to, tokenId, "");
    }

    /**
     * @dev Same as {xref-ERC721-_safeTransfer-address-address-uint256-}[`_safeTransfer`], with an additional `data` parameter which is
     * forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
     */
    function _safeTransfer(address from, address to, uint256 tokenId, bytes memory data) internal virtual {
        _transfer(from, to, tokenId);
        _checkOnERC721Received(from, to, tokenId, data);
    }

    /**
     * @dev Approve `to` to operate on `tokenId`
     *
     * The `auth` argument is optional. If the value passed is non 0, then this function will check that `auth` is
     * either the owner of the token, or approved to operate on all tokens held by this owner.
     *
     * Emits an {Approval} event.
     *
     * Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
     */
    function _approve(address to, uint256 tokenId, address auth) internal {
        _approve(to, tokenId, auth, true);
    }

    /**
     * @dev Variant of `_approve` with an optional flag to enable or disable the {Approval} event. The event is not
     * emitted in the context of transfers.
     */
    function _approve(address to, uint256 tokenId, address auth, bool emitEvent) internal virtual {
        ERC721Storage storage $ = _getERC721Storage();
        // Avoid reading the owner unless necessary
        if (emitEvent || auth != address(0)) {
            address owner = _requireOwned(tokenId);

            // We do not use _isAuthorized because single-token approvals should not be able to call approve
            if (auth != address(0) && owner != auth && !isApprovedForAll(owner, auth)) {
                revert ERC721InvalidApprover(auth);
            }

            if (emitEvent) {
                emit Approval(owner, to, tokenId);
            }
        }

        $._tokenApprovals[tokenId] = to;
    }

    /**
     * @dev Approve `operator` to operate on all of `owner` tokens
     *
     * Requirements:
     * - operator can't be the address zero.
     *
     * Emits an {ApprovalForAll} event.
     */
    function _setApprovalForAll(address owner, address operator, bool approved) internal virtual {
        ERC721Storage storage $ = _getERC721Storage();
        if (operator == address(0)) {
            revert ERC721InvalidOperator(operator);
        }
        $._operatorApprovals[owner][operator] = approved;
        emit ApprovalForAll(owner, operator, approved);
    }

    /**
     * @dev Reverts if the `tokenId` doesn't have a current owner (it hasn't been minted, or it has been burned).
     * Returns the owner.
     *
     * Overrides to ownership logic should be done to {_ownerOf}.
     */
    function _requireOwned(uint256 tokenId) internal view returns (address) {
        address owner = _ownerOf(tokenId);
        if (owner == address(0)) {
            revert ERC721NonexistentToken(tokenId);
        }
        return owner;
    }

    /**
     * @dev Private function to invoke {IERC721Receiver-onERC721Received} on a target address. This will revert if the
     * recipient doesn't accept the token transfer. The call is not executed if the target address is not a contract.
     *
     * @param from address representing the previous owner of the given token ID
     * @param to target address that will receive the tokens
     * @param tokenId uint256 ID of the token to be transferred
     * @param data bytes optional data to send along with the call
     */
    function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory data) private {
        if (to.code.length > 0) {
            try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, data) returns (bytes4 retval) {
                if (retval != IERC721Receiver.onERC721Received.selector) {
                    revert ERC721InvalidReceiver(to);
                }
            } catch (bytes memory reason) {
                if (reason.length == 0) {
                    revert ERC721InvalidReceiver(to);
                } else {
                    /// @solidity memory-safe-assembly
                    assembly {
                        revert(add(32, reason), mload(reason))
                    }
                }
            }
        }
    }
}
合同源代码
文件 33 的 124:EnumerableSet.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.

pragma solidity ^0.8.20;

/**
 * @dev Library for managing
 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
 * types.
 *
 * Sets have the following properties:
 *
 * - Elements are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Elements are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```solidity
 * contract Example {
 *     // Add the library methods
 *     using EnumerableSet for EnumerableSet.AddressSet;
 *
 *     // Declare a set state variable
 *     EnumerableSet.AddressSet private mySet;
 * }
 * ```
 *
 * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
 * and `uint256` (`UintSet`) are supported.
 *
 * [WARNING]
 * ====
 * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
 * unusable.
 * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
 *
 * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
 * array of EnumerableSet.
 * ====
 */
library EnumerableSet {
    // To implement this library for multiple types with as little code
    // repetition as possible, we write it in terms of a generic Set type with
    // bytes32 values.
    // The Set implementation uses private functions, and user-facing
    // implementations (such as AddressSet) are just wrappers around the
    // underlying Set.
    // This means that we can only create new EnumerableSets for types that fit
    // in bytes32.

    struct Set {
        // Storage of set values
        bytes32[] _values;
        // Position is the index of the value in the `values` array plus 1.
        // Position 0 is used to mean a value is not in the set.
        mapping(bytes32 value => uint256) _positions;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function _add(Set storage set, bytes32 value) private returns (bool) {
        if (!_contains(set, value)) {
            set._values.push(value);
            // The value is stored at length-1, but we add 1 to all indexes
            // and use 0 as a sentinel value
            set._positions[value] = set._values.length;
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function _remove(Set storage set, bytes32 value) private returns (bool) {
        // We cache the value's position to prevent multiple reads from the same storage slot
        uint256 position = set._positions[value];

        if (position != 0) {
            // Equivalent to contains(set, value)
            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
            // the array, and then remove the last element (sometimes called as 'swap and pop').
            // This modifies the order of the array, as noted in {at}.

            uint256 valueIndex = position - 1;
            uint256 lastIndex = set._values.length - 1;

            if (valueIndex != lastIndex) {
                bytes32 lastValue = set._values[lastIndex];

                // Move the lastValue to the index where the value to delete is
                set._values[valueIndex] = lastValue;
                // Update the tracked position of the lastValue (that was just moved)
                set._positions[lastValue] = position;
            }

            // Delete the slot where the moved value was stored
            set._values.pop();

            // Delete the tracked position for the deleted slot
            delete set._positions[value];

            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function _contains(Set storage set, bytes32 value) private view returns (bool) {
        return set._positions[value] != 0;
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function _length(Set storage set) private view returns (uint256) {
        return set._values.length;
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function _at(Set storage set, uint256 index) private view returns (bytes32) {
        return set._values[index];
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function _values(Set storage set) private view returns (bytes32[] memory) {
        return set._values;
    }

    // Bytes32Set

    struct Bytes32Set {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _add(set._inner, value);
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _remove(set._inner, value);
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
        return _contains(set._inner, value);
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(Bytes32Set storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
        return _at(set._inner, index);
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
        bytes32[] memory store = _values(set._inner);
        bytes32[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }

    // AddressSet

    struct AddressSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(AddressSet storage set, address value) internal returns (bool) {
        return _add(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(AddressSet storage set, address value) internal returns (bool) {
        return _remove(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(AddressSet storage set, address value) internal view returns (bool) {
        return _contains(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(AddressSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(AddressSet storage set, uint256 index) internal view returns (address) {
        return address(uint160(uint256(_at(set._inner, index))));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(AddressSet storage set) internal view returns (address[] memory) {
        bytes32[] memory store = _values(set._inner);
        address[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }

    // UintSet

    struct UintSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(UintSet storage set, uint256 value) internal returns (bool) {
        return _add(set._inner, bytes32(value));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(UintSet storage set, uint256 value) internal returns (bool) {
        return _remove(set._inner, bytes32(value));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(UintSet storage set, uint256 value) internal view returns (bool) {
        return _contains(set._inner, bytes32(value));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(UintSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(UintSet storage set, uint256 index) internal view returns (uint256) {
        return uint256(_at(set._inner, index));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(UintSet storage set) internal view returns (uint256[] memory) {
        bytes32[] memory store = _values(set._inner);
        uint256[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }
}
合同源代码
文件 34 的 124:FeeConditionBase.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

import {OspContext} from '../../core/base/OspContext.sol';
import {CondErrors} from './libraries/CondErrors.sol';
import {IGovernanceLogic} from '../logics/interfaces/IGovernanceLogic.sol';

/**
 * @title FeeConditionBase
 * @author OpenSocial Protocol
 *
 * @dev This is an abstract contract to be inherited from by conditions that require basic fee functionality. It
 * contains getters for condition globals parameters as well as a validation function to check expected data.
 */
abstract contract FeeConditionBase is OspContext {
    function _validateDataIsExpected(
        bytes calldata data,
        address currency,
        uint256 amount
    ) internal pure {
        (address decodedCurrency, uint256 decodedAmount) = abi.decode(data, (address, uint256));
        if (decodedAmount != amount || decodedCurrency != currency)
            revert CondErrors.ConditionDataMismatch();
    }

    function _tokenWhitelisted(address token) internal view returns (bool) {
        return IGovernanceLogic(OSP).isTokenWhitelisted(token);
    }
}
合同源代码
文件 35 的 124:FixedFeeCommunityCond.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

import {IERC721} from '@openzeppelin/contracts/token/ERC721/IERC721.sol';
import {CommunityCondBase} from '../../base/CommunityCondBase.sol';
import {Payment} from '../../../libraries/Payment.sol';
import {ECDSA} from '@openzeppelin/contracts/utils/cryptography/ECDSA.sol';
import {CondErrors} from '../libraries/CondErrors.sol';
import {CondDataTypes} from '../libraries/CondDataTypes.sol';
import {CondHelpers} from '../libraries/CondHelpers.sol';

/**
 * @title FixedFeeCommunityCond
 * @author OpenSocial Protocol
 *
 * @dev This contract specifies that pay the specified amount of ETH to create the community.
 * The amount of ETH paid is related to the handle length of the community.
 */
contract FixedFeeCommunityCond is CommunityCondBase {
    event FixFeeCondDataSet(CondDataTypes.FixedFeeCondData data, uint256 timestamp);
    event FixFeePaid(address indexed to, uint256 price, string handle, uint256 timestamp);

    CondDataTypes.FixedFeeCondData public fixedFeeCondData;

    constructor(address osp) CommunityCondBase(osp) {}

    /**
     * @dev process create community,if the slotNFT is used, revert.
     */
    function _processCreateCommunity(
        address to,
        string calldata handle,
        bytes calldata /*data*/
    ) internal override {
        /// @dev if createStartTime is not set, indicates no initialization.
        if (
            block.timestamp < fixedFeeCondData.createStartTime ||
            fixedFeeCondData.createStartTime == 0
        ) {
            revert CondErrors.NotCreateTime();
        }
        uint256 price = CondHelpers.getHandleETHPrice(handle, fixedFeeCondData);
        _charge(price, to);
        emit FixFeePaid(to, price, handle, block.timestamp);
    }

    function setFixedFeeCondData(
        CondDataTypes.FixedFeeCondData calldata data
    ) external onlyOperation {
        fixedFeeCondData = CondDataTypes.FixedFeeCondData({
            price1Letter: data.price1Letter,
            price2Letter: data.price2Letter,
            price3Letter: data.price3Letter,
            price4Letter: data.price4Letter,
            price5Letter: data.price5Letter,
            price6Letter: data.price6Letter,
            price7ToMoreLetter: data.price7ToMoreLetter,
            createStartTime: data.createStartTime
        });
        emit FixFeeCondDataSet(data, block.timestamp);
    }

    /**
     * @dev Get the handle price based on the length of the handle.
     */
    function getHandlePrice(string calldata handle) external view returns (uint256) {
        return CondHelpers.getHandleETHPrice(handle, fixedFeeCondData);
    }

    function _charge(uint256 price, address to) internal virtual {
        if (msg.value < price) {
            revert CondErrors.InsufficientPayment();
        }
        uint256 overpayment;
        unchecked {
            overpayment = msg.value - price;
        }
        if (overpayment > 0) {
            Payment.payNative(to, overpayment);
        }
        address treasure = OSP.getTreasureAddress();
        require(treasure != address(0), 'Invalid treasure');
        Payment.payNative(treasure, price);
    }
}
合同源代码
文件 36 的 124:FollowSBT.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

import '../interfaces/IFollowSBT.sol';
import '../interfaces/IFollowCondition.sol';
import './logics/interfaces/OspClient.sol';
import '../libraries/OspErrors.sol';
import '../libraries/OspEvents.sol';
import './base/OspSBTBase.sol';

/**
 * @title FollowSBT
 * @author OpenSocial Protocol
 * @dev This contract is the NFT that is minted upon following a given profile. It is cloned upon first follow for a
 * given profile.
 */
contract FollowSBT is OspSBTBase, IFollowSBT {
    address public immutable OSP;

    uint256 internal _profileId;
    uint256 internal _tokenIdCounter;

    // We create the FollowSBT with the pre-computed OSP address before deploying the osp.
    constructor(address osp) {
        if (osp == address(0)) revert OspErrors.InitParamsInvalid();
        OSP = osp;
    }

    /// @inheritdoc IFollowSBT
    function initialize(
        uint256 profileId,
        string calldata name,
        string calldata symbol
    ) external override {
        if (msg.sender != OSP) revert OspErrors.NotOSP();
        _profileId = profileId;
        super._initialize(name, symbol);
        emit OspEvents.FollowSBTInitialized(profileId, block.timestamp);
    }

    /// @inheritdoc IFollowSBT
    function mint(address to) external override returns (uint256) {
        if (msg.sender != OSP) revert OspErrors.NotOSP();
        unchecked {
            uint256 tokenId = ++_tokenIdCounter;
            _mint(to, tokenId);
            return tokenId;
        }
    }

    /**
     * @dev This returns the follow NFT URI fetched from the osp.
     */
    function tokenURI(uint256 tokenId) public view override returns (string memory) {
        return OspClient(OSP).getFollowSBTURI(_profileId, tokenId);
    }

    /**
     * @dev Upon transfers, we move the appropriate delegations, and emit the transfer event in the osp.
     */
    function _afterTokenTransfer(address from, address to, uint256 tokenId) internal override {
        super._afterTokenTransfer(from, to, tokenId);
        OspClient(OSP).emitFollowSBTTransferEvent(_profileId, tokenId, from, to);
    }
}
合同源代码
文件 37 的 124:FollowSBTProxy.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

import {IGovernanceLogic} from '../core/logics/interfaces/IGovernanceLogic.sol';
import {Proxy} from '@openzeppelin/contracts/proxy/Proxy.sol';
import {Address} from '@openzeppelin/contracts/utils/Address.sol';

contract FollowSBTProxy is Proxy {
    using Address for address;
    address immutable OSP;

    constructor(bytes memory data) {
        OSP = msg.sender;
        IGovernanceLogic(msg.sender).getFollowSBTImpl().functionDelegateCall(data);
    }

    function _implementation() internal view override returns (address) {
        return IGovernanceLogic(OSP).getFollowSBTImpl();
    }
}
合同源代码
文件 38 的 124:GovernanceLogic.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

import './OspLogicBase.sol';
import './interfaces/IGovernanceLogic.sol';
import '../../libraries/OspDataTypes.sol';
import '@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol';
import '@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol';
import '../../interfaces/IERC6551Registry.sol';
import '../../libraries/Constants.sol';
import '../../upgradeability/CommunityAccountProxy.sol';
import '@thirdweb-dev/contracts/extension/upgradeable/ContractMetadata.sol';

/**
 * @title GovernanceLogic
 * @author OpenSocial Protocol
 * @dev GovernanceLogic is the contract that manages the governance of the protocol.
 */
contract GovernanceLogic is
    IGovernanceLogic,
    OspLogicBase,
    AccessControlUpgradeable,
    ContractMetadata
{
    /*///////////////////////////////////////////////////////////////
                        Public functions
    //////////////////////////////////////////////////////////////*/
    /// @inheritdoc IGovernanceLogic
    function initialize(
        string calldata name,
        string calldata symbol,
        address followSBTImpl,
        address joinNFTImpl,
        address communityNFT
    ) external override initializer {
        _grantRole(DEFAULT_ADMIN_ROLE, _msgSender());
        GovernanceStorage storage governanceStorage = _getGovernanceStorage();
        governanceStorage._name = name;
        governanceStorage._symbol = symbol;
        governanceStorage._followSBTImpl = followSBTImpl;
        governanceStorage._joinNFTImpl = joinNFTImpl;
        governanceStorage._communityNFT = communityNFT;
        address communityAccountProxy = address(new CommunityAccountProxy());
        governanceStorage._communityAccountProxy = communityAccountProxy;
        _setState(OspDataTypes.ProtocolState.Paused);
        emit OspEvents.OSPInitialized(
            name,
            symbol,
            followSBTImpl,
            joinNFTImpl,
            communityNFT,
            communityAccountProxy,
            block.timestamp
        );
    }

    /// @inheritdoc IGovernanceLogic
    function setState(
        OspDataTypes.ProtocolState newState
    ) external onlyRole(Constants.STATE_ADMIN) {
        _setState(newState);
    }

    /// @inheritdoc IGovernanceLogic
    function whitelistApp(address app, bool whitelist) external onlyRole(Constants.APP_ADMIN) {
        _getGovernanceStorage()._appWhitelisted[app] = whitelist;
        emit OspEvents.AppWhitelisted(app, whitelist, block.timestamp);
    }

    // @inheritdoc IGovernanceLogic
    function whitelistToken(
        address token,
        bool whitelist
    ) external override onlyRole(Constants.OPERATION) {
        _getGovernanceStorage()._tokenWhitelisted[token] = whitelist;
        emit OspEvents.TokenWhitelisted(token, whitelist, block.timestamp);
    }

    /// @inheritdoc IGovernanceLogic
    function reserveCommunityHandle(
        string calldata handle,
        bool isReserve
    ) external override onlyRole(Constants.SUPER_COMMUNITY_CREATOR) {
        _getGovernanceStorage()._reserveCommunityHandleHash[keccak256(bytes(handle))] = isReserve;
        emit OspEvents.CommunityHandleReserve(
            keccak256(bytes(handle)),
            isReserve,
            handle,
            block.timestamp
        );
    }

    /// @inheritdoc IGovernanceLogic
    function setBaseURI(string calldata baseURI) external override onlyRole(Constants.GOVERNANCE) {
        _getGovernanceStorage()._baseURI = baseURI;
        emit OspEvents.BaseURISet(baseURI, block.timestamp);
    }

    /// @inheritdoc IGovernanceLogic
    function setERC6551AccountImpl(
        address accountImpl
    ) external override onlyRole(Constants.GOVERNANCE) {
        _getGovernanceStorage()._erc6551AccountImpl = accountImpl;
        emit OspEvents.ERC6551AccountImplSet(accountImpl, block.timestamp);
    }

    function setJoinNFTImpl(address joinNFTImpl) external override onlyRole(Constants.GOVERNANCE) {
        _getGovernanceStorage()._joinNFTImpl = joinNFTImpl;
        emit OspEvents.JoinNFTImplSet(joinNFTImpl, block.timestamp);
    }

    /// @inheritdoc IGovernanceLogic
    function updateMetadata() external override onlyRole(Constants.GOVERNANCE) {
        emit BatchMetadataUpdate(1, type(uint256).max);
    }

    function setTreasureAddress(address treasure) external override onlyRole(Constants.GOVERNANCE) {
        if (treasure == address(0)) revert OspErrors.InvalidAddress();
        _getGovernanceStorage()._treasure = treasure;
    }

    function setJoinNFTRoyalty(
        uint128 royaltyFraction,
        uint128 ospTreasureFraction
    ) external onlyRole(Constants.GOVERNANCE) {
        if (
            royaltyFraction > Constants.ROYALTY_DENOMINATOR ||
            ospTreasureFraction > Constants.ROYALTY_DENOMINATOR
        ) {
            revert OspErrors.InvalidParam();
        }
        _getGovernanceStorage()._joinNFTRoyaltyInfo = OspDataTypes.RoyaltyInfo({
            royaltyFraction: royaltyFraction,
            ospTreasureFraction: ospTreasureFraction
        });
    }

    /*///////////////////////////////////////////////////////////////
                        Public read functions
    //////////////////////////////////////////////////////////////*/

    /// @inheritdoc IGovernanceLogic
    function isAppWhitelisted(address app) external view override returns (bool) {
        return _getGovernanceStorage()._appWhitelisted[app];
    }

    // @inheritdoc IGovernanceLogic
    function isTokenWhitelisted(address token) external view override returns (bool) {
        return _getGovernanceStorage()._tokenWhitelisted[token];
    }

    /// @inheritdoc IGovernanceLogic
    function isReserveCommunityHandle(
        string calldata handle
    ) external view override returns (bool) {
        return _getGovernanceStorage()._reserveCommunityHandleHash[keccak256(bytes(handle))];
    }

    /// @inheritdoc IGovernanceLogic
    function getFollowSBTImpl() external view override returns (address) {
        return _getGovernanceStorage()._followSBTImpl;
    }

    /// @inheritdoc IGovernanceLogic
    function getJoinNFTImpl() external view override returns (address) {
        return _getGovernanceStorage()._joinNFTImpl;
    }

    /// @inheritdoc IGovernanceLogic
    function getCommunityNFT() external view override returns (address) {
        return _getGovernanceStorage()._communityNFT;
    }

    /// @inheritdoc IGovernanceLogic
    function getERC6551AccountImpl() external view override returns (address) {
        return _getGovernanceStorage()._erc6551AccountImpl;
    }

    /// @inheritdoc IGovernanceLogic
    function getState() external view override returns (OspDataTypes.ProtocolState) {
        return _getState();
    }

    /// @inheritdoc IGovernanceLogic
    function getBaseURI() external view override returns (string memory) {
        return _getGovernanceStorage()._baseURI;
    }

    /// @inheritdoc IERC5267
    function eip712Domain()
        external
        view
        override
        returns (
            bytes1 fields,
            string memory name,
            string memory version,
            uint256 chainId,
            address verifyingContract,
            bytes32 salt,
            uint256[] memory extensions
        )
    {
        return (
            hex'0f', // 01111
            _getGovernanceStorage()._name,
            '1',
            block.chainid,
            address(this),
            bytes32(0),
            new uint256[](0)
        );
    }

    function getTreasureAddress() external view override returns (address) {
        return _getGovernanceStorage()._treasure;
    }

    function joinNFTRoyaltyInfo()
        external
        view
        returns (uint128 royaltyFraction, uint128 ospTreasureFraction)
    {
        OspDataTypes.RoyaltyInfo memory joinNFTRoyalty = _getGovernanceStorage()
            ._joinNFTRoyaltyInfo;
        return (joinNFTRoyalty.royaltyFraction, joinNFTRoyalty.ospTreasureFraction);
    }

    function _canSetContractURI() internal view override returns (bool) {
        return _hashRole(Constants.GOVERNANCE, _msgSender());
    }
}
合同源代码
文件 39 的 124:HoldERC721ActivityCond.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.20;

import {ActivityConditionBase} from 'contracts/core/base/ActivityConditionBase.sol';
import {IHoldERC721ActivityCond} from './interfaces/IHoldERC721ActivityCond.sol';
import {CondErrors} from '../libraries/CondErrors.sol';

import {IERC721} from '@openzeppelin/contracts/token/ERC721/IERC721.sol';

contract HoldERC721ActivityCond is ActivityConditionBase, IHoldERC721ActivityCond {
    mapping(uint256 => HoldERC721Data) internal _dataByCommunity;

    constructor(
        address osp,
        address compositeActivityCondition
    ) ActivityConditionBase(osp, compositeActivityCondition) {}

    function _initializeCommunityActivityCondition(
        uint256 communityId,
        bytes calldata data
    ) internal override {
        (address erc721Address, uint96 minAmount) = abi.decode(data, (address, uint96));
        _dataByCommunity[communityId] = HoldERC721Data(erc721Address, minAmount);
    }

    function _processActivity(
        uint256 communityId,
        address profileOwner,
        uint256 profileId
    ) internal override {
        HoldERC721Data memory data = _dataByCommunity[communityId];
        uint256 balance = IERC721(data.tokenAddress).balanceOf(profileOwner);
        if (balance < data.amount) revert CondErrors.NotEnoughERC721Balance();
    }

    // @inheritdoc IHoldERC721ActivityCond
    function getHoldERC721Data(uint256 communityId) external view returns (HoldERC721Data memory) {
        return _dataByCommunity[communityId];
    }

    // @inheritdoc IERC165
    function supportsInterface(bytes4 interfaceId) public pure override returns (bool) {
        return
            ActivityConditionBase.supportsInterface(interfaceId) ||
            interfaceId == type(IHoldERC721ActivityCond).interfaceId;
    }
}
合同源代码
文件 40 的 124:HoldERC721ViewPrivacyCond.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

import {ViewPrivacyConditionBase} from 'contracts/core/base/ViewPrivacyConditionBase.sol';
import {IHoldERC721ViewPrivacyCond} from './interfaces/IHoldERC721ViewPrivacyCond.sol';

import {IERC721} from '@openzeppelin/contracts/token/ERC721/IERC721.sol';

contract HoldERC721ViewPrivacyCond is ViewPrivacyConditionBase, IHoldERC721ViewPrivacyCond {
    mapping(uint256 => HoldERC721Data) _dataByCommunity;

    constructor(
        address osp,
        address compositeCondition
    ) ViewPrivacyConditionBase(osp, compositeCondition) {}

    function _initializeCommunityViewPrivacyCondition(
        uint256 communityId,
        bytes calldata data
    ) internal override {
        (address erc721Address, uint96 minAmount) = abi.decode(data, (address, uint96));
        _dataByCommunity[communityId] = HoldERC721Data(erc721Address, minAmount);
    }

    function _isViewPrivacyAllowed(
        uint256 communityId,
        address profileOwner,
        uint256 profileId
    ) internal view override returns (bool) {
        HoldERC721Data memory data = _dataByCommunity[communityId];
        uint256 balance = IERC721(data.tokenAddress).balanceOf(profileOwner);
        return balance >= data.amount;
    }

    // @inheritdoc IRoleViewPrivacyCond
    function getHoldERC721Data(uint256 communityId) external view returns (HoldERC721Data memory) {
        return _dataByCommunity[communityId];
    }

    // @inheritdoc IERC165
    function supportsInterface(bytes4 interfaceId) public pure override returns (bool) {
        return
            ViewPrivacyConditionBase.supportsInterface(interfaceId) ||
            interfaceId == type(IHoldERC721ViewPrivacyCond).interfaceId;
    }
}
合同源代码
文件 41 的 124:HoldTokenJoinCond.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

import {CondErrors} from '../libraries/CondErrors.sol';
import {JoinConditionBase} from '../../base/JoinConditionBase.sol';

/**
 * @title HoldTokenJoinCond
 * @param token token address,if token is address(0) means native token.
 * @param amount token amount.
 */
struct CommunityData {
    address token;
    uint256 amount;
}

/**
 * @title IToken
 * @dev Used to check erc20 or erc721 token balance
 */
interface IToken {
    function balanceOf(address owner) external view returns (uint256);
}

/**
 * @title HoldTokenJoinCond
 * @author OpenSocial
 * @dev This condition allows users to join a community by holding a certain amount of tokens.
 */
contract HoldTokenJoinCond is JoinConditionBase {
    mapping(uint256 => CommunityData) _dataByCommunity;

    constructor(address osp) JoinConditionBase(osp) {}

    function _initializeCommunityJoinCondition(
        uint256 communityId,
        bytes calldata data
    ) internal override {
        (address _token, uint256 _amount) = abi.decode(data, (address, uint256));
        _dataByCommunity[communityId].token = _token;
        _dataByCommunity[communityId].amount = _amount;
    }

    function _processJoin(
        address joiner,
        uint256 communityId,
        bytes calldata data
    ) internal override nonPayable {
        (data); //unused
        address token = _dataByCommunity[communityId].token;
        if (token == address(0) && joiner.balance >= _dataByCommunity[communityId].amount) {
            //native token
            return;
        } else if (
            token != address(0) &&
            IToken(token).balanceOf(joiner) >= _dataByCommunity[communityId].amount
        ) {
            //ERC20 or ERC721 token
            return;
        }
        revert CondErrors.JoinInvalid();
    }

    function _processTransferJoinNFT(
        uint256 communityId,
        uint256 joinNFTId,
        address from,
        address to
    ) internal override {
        if (from != address(0) && to != address(0)) {
            revert CondErrors.JoinNFTTransferInvalid();
        }
    }

    /**
     * @dev Returns the community data for a given community, or an empty struct if that community was not initialized
     * with this condition.
     */
    function getCommunityData(uint256 communityId) external view returns (CommunityData memory) {
        return _dataByCommunity[communityId];
    }
}
合同源代码
文件 42 的 124:IAccessControl.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/IAccessControl.sol)

pragma solidity ^0.8.20;

/**
 * @dev External interface of AccessControl declared to support ERC165 detection.
 */
interface IAccessControl {
    /**
     * @dev The `account` is missing a role.
     */
    error AccessControlUnauthorizedAccount(address account, bytes32 neededRole);

    /**
     * @dev The caller of a function is not the expected one.
     *
     * NOTE: Don't confuse with {AccessControlUnauthorizedAccount}.
     */
    error AccessControlBadConfirmation();

    /**
     * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
     *
     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
     * {RoleAdminChanged} not being emitted signaling this.
     */
    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);

    /**
     * @dev Emitted when `account` is granted `role`.
     *
     * `sender` is the account that originated the contract call, an admin role
     * bearer except when using {AccessControl-_setupRole}.
     */
    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Emitted when `account` is revoked `role`.
     *
     * `sender` is the account that originated the contract call:
     *   - if using `revokeRole`, it is the admin role bearer
     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
     */
    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) external view returns (bool);

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {AccessControl-_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) external view returns (bytes32);

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function grantRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function revokeRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been granted `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `callerConfirmation`.
     */
    function renounceRole(bytes32 role, address callerConfirmation) external;
}
合同源代码
文件 43 的 124:IActivityCondition.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

/**
 * @title IActivityCondition
 * @author Osp Protocol
 *
 * @dev This is the standard interface for all Osp-compatible ActivityConditions.
 */
interface IActivityCondition {
    /**
     * @dev Initializes a post Condition for a given OSP community. This can only be called by the osp contract.
     *
     * @param communityId The token ID of the community to initialize this post condition for.
     * @param data initialization data.
     */
    function initializeCommunityActivityCondition(
        uint256 communityId,
        bytes calldata data
    ) external;

    /**
     * @dev Processes a given post, this can only be called from the OSP contract.
     *
     * @param communityId The token ID of the community being posted to.
     * @param profileId The token ID of the profile being posted.
     */
    function processActivity(uint256 communityId, address profileOwner, uint256 profileId) external;
}
合同源代码
文件 44 的 124:IActivityExtension.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

interface IActivityExtension {
    function initializeActivityExtension(
        uint256 profileId,
        uint256 contentId,
        bytes calldata initData
    ) external payable;
}
合同源代码
文件 45 的 124:IActivityExtensionQueryable.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

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

interface IActivityExtensionQueryable is IActivityExtension {
    function getExtensionData(
        uint256 profileId,
        uint256 contentId
    ) external view returns (bytes memory);
}
合同源代码
文件 46 的 124:IBeacon.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/beacon/IBeacon.sol)

pragma solidity ^0.8.20;

/**
 * @dev This is the interface that {BeaconProxy} expects of its beacon.
 */
interface IBeacon {
    /**
     * @dev Must return an address that can be used as a delegate call target.
     *
     * {UpgradeableBeacon} will check that this address is a contract.
     */
    function implementation() external view returns (address);
}
合同源代码
文件 47 的 124:ICommunityCondition.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

/**
 * @title ICommunityCondition
 * @author Opensocial Protocol
 *
 * @dev This is the standard interface for all Osp-compatible Conditions.
 * Conditions are used to check if a community can be created.
 */
interface ICommunityCondition {
    /**
     * @dev Checks if a community can be created.
     * @param to community creator.
     * @param data condition data for checking.
     */
    function processCreateCommunity(
        address to,
        string calldata handle,
        bytes calldata data
    ) external payable;
}
合同源代码
文件 48 的 124:ICommunityLogic.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

import '../../../libraries/OspDataTypes.sol';

/**
 * @title ICommunityLogic
 * @author OpenSocial Protocol
 *
 * @dev This is the interface for the ICommunityLogic contract.
 */
interface ICommunityLogic {
    /**
     * @dev Creates a community with the specified parameters, minting a community NFT to the msg sender.
     *
     * @param vars A CreateCommunityData struct containing the needed parameters.
     *
     * @return uint256 An integer representing the community's ID.
     */
    function createCommunity(
        OspDataTypes.CreateCommunityData calldata vars
    ) external payable returns (uint256);

    /**
     * @dev Creates a community and set activity condition, minting a community NFT to the msg sender.
     *
     * @param vars A CreateCommunityData struct containing the needed parameters.
     * @param activityConditionInitCode The data to be passed to the activity condition for initialization.
     * @param viewPrivacyConditionInitCode The data to be passed to the view privacy condition for initialization.
     *
     * @return uint256 An integer representing the community's ID.
     */
    function createCommunity(
        OspDataTypes.CreateCommunityData calldata vars,
        bytes calldata activityConditionInitCode,
        bytes calldata viewPrivacyConditionInitCode
    ) external payable returns (uint256);

    /**
     * @dev Sets a community's join condition, must be called by the community owner or admin.
     * @param communityId The ID of the community to set the join condition for.
     * @param joinConditionInitCode The data to be passed to the join condition for initialization.
     */
    function setJoinCondition(uint256 communityId, bytes calldata joinConditionInitCode) external;

    /**
     * @dev Sets a community's activity condition, must be called by the community owner or admin.
     * @param communityId The ID of the community to set the activity condition for.
     * @param activityConditionInitCode The data to be passed to the activity condition for initialization.
     */
    function setActivityCondition(
        uint256 communityId,
        bytes calldata activityConditionInitCode
    ) external;

    /**
     * @dev Sets a community's view privacy condition, must be called by the community owner or admin.
     * @param communityId The ID of the community to set the view privacy condition for.
     * @param viewPrivacyConditionInitCode The data to be passed to the view privacy condition for initialization.
     */
    function setViewPrivacyCondition(
        uint256 communityId,
        bytes calldata viewPrivacyConditionInitCode
    ) external;

    /**
     * @dev Checks if a profile is allowed to view a community privacy content, based on the view privacy condition.
     * @param communityId The ID of the community to check.
     * @param profileId The ID of the profile to check.
     * @return bool A boolean indicating whether the profile is allowed to view the community privacy content or not.
     */
    function isViewPrivacyAllowed(
        uint256 communityId,
        uint256 profileId
    ) external view returns (bool);

    /**
     * @dev Updates a community's tags, must be called by the community owner or admin.
     * @param communityId The ID of the community to set the tags for.
     * @param tags The new tags for the community.
     */
    function updateTags(uint256 communityId, string[] calldata tags) external;

    /**
     * @dev Sets a community's extension whitelist, must be called by the community owner or admin.
     * @param communityId The ID of the community to set the extension whitelist for.
     * @param extension The address of the extension to whitelist.
     * @param isWhitelist A boolean indicating whether the extension should be whitelisted or not.
     */
    function setCommunityActivityExtensionWhitelist(
        uint256 communityId,
        address extension,
        bool isWhitelist
    ) external;

    /**
     * @dev emits a CommunityNFTTransfer event to facilitate the monitoring of events.
     *
     * @param communityId is CommunityNFT being transferred's token ID.
     * @param from The address the collectNFT is being transferred from.
     * @param to The address the collectNFT is being transferred to.
     */
    function emitCommunityNFTTransferEvent(uint256 communityId, address from, address to) external;

    /**
     * @dev Returns the struct with a community.
     *
     * @param communityId The ID of the community.
     *
     * @return string The struct of community.
     */
    function getCommunity(
        uint256 communityId
    ) external view returns (OspDataTypes.CommunityStruct memory);

    /**
     * @dev Returns the joinNFT associated with a given community, if any.
     * @param communityId The unique ID of the community to query the joinNFT for.
     * @return address The joinNFT associated with the given profile.
     */
    function getJoinNFT(uint256 communityId) external view returns (address);

    /**
     * @dev Returns the join condition associated witha  given community, if any.
     * @param communityId The unique ID of the community to query the join condition for.
     * @return address The address of the join condition associated with the given community.
     */
    function getJoinCondition(uint256 communityId) external view returns (address);

    /**
     * @dev Returns the activity condition associated witha  given community, if any.
     * @param communityId The unique ID of the community to query the activity condition for.
     * @return address The address of the activity condition associated with the given community.
     */
    function getActivityCondition(uint256 communityId) external view returns (address);

    /**
     * @dev Returns the tokenURI associated with a given community, if any.
     *
     * @param communityId The unique ID of the community to query the tokenURI for.
     * @return string The tokenURI associated with the given community.
     */
    function getCommunityTokenURI(uint256 communityId) external view returns (string memory);

    /**
     * @dev Returns the community ID associated with a given community handle, if any.
     *
     * @param handle The handle of the community to query the ID for.
     * @return uint256 The ID of the community associated with the given handle.
     */
    function getCommunityIdByHandle(string calldata handle) external view returns (uint256);

    /**
     * @dev Returns community nft 6551 account address.
     * @param communityId community nft token id
     */
    function getCommunityAccount(uint256 communityId) external view returns (address);

    /**
     * @dev Returns community nft 6551 account address.
     * @param handle community handle.
     */
    function getCommunityAccount(string calldata handle) external view returns (address);

    /**
     * @dev Returns the extension whitelist associated with a given community, if any.
     * @param communityId The unique ID of the community to query the extension whitelist for.
     */
    function getCommunityActivityExtensionWhitelist(
        uint256 communityId
    ) external view returns (address[] memory extensions);
}
合同源代码
文件 49 的 124:ICommunityNFT.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

/**
 * @title ICommunityNFT
 * @author OpenSocial Protocol
 *
 * @dev This is the standard interface for all OSP-compatible CollectConditions.
 */
interface ICommunityNFT {
    /**
     * @dev Mints a collect NFT to the specified address. This can only be called by the osp;
     *
     * @param to The address to mint the NFT to.
     *
     * @return uint256 An interger representing the minted token ID.
     */
    function mint(address to) external returns (uint256);

    /**
     * @dev updates the metadata for the NFT.
     */
    function updateMetadata() external;
}
合同源代码
文件 50 的 124:ICompositeActivityCond.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

import {IActivityCondition} from 'contracts/interfaces/IActivityCondition.sol';

/**
 * @title ICompositeActivityCond
 * @dev This contract allows for the creation of composite activity conditions,
 *      which can be used to combine multiple activity conditions using logical operators.
 */
interface ICompositeActivityCond is IActivityCondition {
    /**
     * @dev Enum representing the available logical operators for combining activity conditions.
     */
    enum OperatorEnum {
        AND,
        OR
    }

    /**
     * @dev Struct representing the composite activity condition data.
     * @param operator The logical operator used to combine the activity conditions.
     * @param conditions The array of activity conditions to be combined.
     */
    struct CompositeData {
        OperatorEnum operator;
        IActivityCondition[] conditions;
    }

    /**
     * @dev Returns the composite activity condition data for the specified community.
     * @param communityId The ID of the community.
     * @return The composite activity condition data.
     */
    function getCompositeData(uint256 communityId) external view returns (CompositeData memory);
}
合同源代码
文件 51 的 124:ICompositeViewPrivacyCond.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

import {IViewPrivacyCondition} from 'contracts/interfaces/IViewPrivacyCondition.sol';

/**
 * @title ICompositeViewPrivacyCond.sol
 * @dev This contract allows for the creation of composite privacy conditions,
 *      which are combinations of other privacy conditions.
 */
interface ICompositeViewPrivacyCond is IViewPrivacyCondition {
    /**
     * @dev Enum representing the available logical operators for combining view privacy conditions.
     */
    enum OperatorEnum {
        AND,
        OR
    }

    /**
     * @dev Struct representing the composite view privacy condition data.
     * @param operator The logical operator used to combine the conditions.
     * @param conditions The array of view privacy conditions to be combined.
     */
    struct CompositeData {
        OperatorEnum operator;
        IViewPrivacyCondition[] conditions;
    }

    /**
     * @dev Returns the composite view privacy condition data for a given community.
     * @param communityId The ID of the community.
     * @return The composite view privacy condition data.
     */
    function getCompositeData(uint256 communityId) external view returns (CompositeData memory);
}
合同源代码
文件 52 的 124:IContentLogic.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

import '../../../libraries/OspDataTypes.sol';

/**
 * @title IContentLogic
 * @author OpenSocial Protocol
 *
 * @dev This is the interface for the ContentLogic contract.
 */
interface IContentLogic {
    /**
     * @dev Create an activity.
     *
     * @param vars A CreateActivityData struct.
     *
     * @return contentId  Activity's content ID.
     */
    function createActivity(
        OspDataTypes.CreateActivityData calldata vars
    ) external payable returns (uint256 contentId);

    /**
     * @dev Create an activity with a signature.
     *
     * @param vars A CreateActivityData struct.
     * @param sig A EIP712Signature struct.
     *
     * @return contentId Activity's content ID.
     */
    function createActivityWithSig(
        OspDataTypes.CreateActivityData calldata vars,
        OspDataTypes.EIP712Signature calldata sig
    ) external returns (uint256 contentId);

    /**
     * @dev Create a comment.
     *
     * @param vars A CreateCommentData struct.
     *
     * @return contentId Comment's content ID.
     */
    function createComment(
        OspDataTypes.CreateCommentData calldata vars
    ) external payable returns (uint256 contentId);

    /**
     * @dev Create a comment with a signature.
     *
     * @param vars A CreateCommentData struct.
     * @param sig A EIP712Signature struct.
     *
     * @return contentId  Comment's content ID.
     */
    function createCommentWithSig(
        OspDataTypes.CreateCommentData calldata vars,
        OspDataTypes.EIP712Signature calldata sig
    ) external returns (uint256 contentId);

    /**
     * @dev Create an open reaction.
     *
     * @param vars A CreateOpenReactionData struct.
     */
    function createOpenReaction(OspDataTypes.CreateOpenReactionData calldata vars) external payable;

    /**
     * @dev Create an open reaction with a signature.
     *
     * @param vars A CreateOpenReactionData struct.
     * @param sig A EIP712Signature struct.
     */
    function createOpenReactionWithSig(
        OspDataTypes.CreateOpenReactionData calldata vars,
        OspDataTypes.EIP712Signature calldata sig
    ) external;

    /**
     * @dev Create a megaphone.
     *
     * @param vars A CreateMegaphoneData struct.
     *
     * @return megaphoneId Megaphone's ID.
     */
    function createMegaphone(
        OspDataTypes.CreateMegaphoneData calldata vars
    ) external payable returns (uint256 megaphoneId);

    /// ************************
    /// *****VIEW FUNCTIONS*****
    /// ************************

    /**
     * @dev Returns the content count for a given profile.
     *
     * @param profileId The profile ID to query.
     *
     * @return uint256 Content count for the given profile.
     */
    function getContentCount(uint256 profileId) external view returns (uint256);

    /**
     * @dev Returns the content struct for a given content.
     *
     * @param profileId The profile ID that published the content to query.
     * @param contentId The content ID of the content to query.
     *
     * @return ContentStruct The content struct associated with the queried content.
     */
    function getContent(
        uint256 profileId,
        uint256 contentId
    ) external view returns (OspDataTypes.ContentStruct memory);

    /**
     * @dev Returns the community's ID by content.
     *
     * @param profileId The profile ID that published the content to query.
     * @param contentId The content ID of the content to query.
     *
     * @return uint256 community's ID.
     */
    function getCommunityIdByContent(
        uint256 profileId,
        uint256 contentId
    ) external view returns (uint256);
}
合同源代码
文件 53 的 124:IContractMetadata.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;

/// @author thirdweb

/**
 *  Thirdweb's `ContractMetadata` is a contract extension for any base contracts. It lets you set a metadata URI
 *  for you contract.
 *
 *  Additionally, `ContractMetadata` is necessary for NFT contracts that want royalties to get distributed on OpenSea.
 */

interface IContractMetadata {
    /// @dev Returns the metadata URI of the contract.
    function contractURI() external view returns (string memory);

    /**
     *  @dev Sets contract URI for the storefront-level metadata of the contract.
     *       Only module admin can call this function.
     */
    function setContractURI(string calldata _uri) external;

    /// @dev Emitted when the contract URI is updated.
    event ContractURIUpdated(string prevURI, string newURI);
}
合同源代码
文件 54 的 124:IERC1155Receiver.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC1155/IERC1155Receiver.sol)

pragma solidity ^0.8.20;

import {IERC165} from "../../utils/introspection/IERC165.sol";

/**
 * @dev Interface that must be implemented by smart contracts in order to receive
 * ERC-1155 token transfers.
 */
interface IERC1155Receiver is IERC165 {
    /**
     * @dev Handles the receipt of a single ERC1155 token type. This function is
     * called at the end of a `safeTransferFrom` after the balance has been updated.
     *
     * NOTE: To accept the transfer, this must return
     * `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
     * (i.e. 0xf23a6e61, or its own function selector).
     *
     * @param operator The address which initiated the transfer (i.e. msg.sender)
     * @param from The address which previously owned the token
     * @param id The ID of the token being transferred
     * @param value The amount of tokens being transferred
     * @param data Additional data with no specified format
     * @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed
     */
    function onERC1155Received(
        address operator,
        address from,
        uint256 id,
        uint256 value,
        bytes calldata data
    ) external returns (bytes4);

    /**
     * @dev Handles the receipt of a multiple ERC1155 token types. This function
     * is called at the end of a `safeBatchTransferFrom` after the balances have
     * been updated.
     *
     * NOTE: To accept the transfer(s), this must return
     * `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
     * (i.e. 0xbc197c81, or its own function selector).
     *
     * @param operator The address which initiated the batch transfer (i.e. msg.sender)
     * @param from The address which previously owned the token
     * @param ids An array containing ids of each token being transferred (order and length must match values array)
     * @param values An array containing amounts of each token being transferred (order and length must match ids array)
     * @param data Additional data with no specified format
     * @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed
     */
    function onERC1155BatchReceived(
        address operator,
        address from,
        uint256[] calldata ids,
        uint256[] calldata values,
        bytes calldata data
    ) external returns (bytes4);
}
合同源代码
文件 55 的 124:IERC1271.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC1271.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC1271 standard signature validation method for
 * contracts as defined in https://eips.ethereum.org/EIPS/eip-1271[ERC-1271].
 */
interface IERC1271 {
    /**
     * @dev Should return whether the signature provided is valid for the provided data
     * @param hash      Hash of the data to be signed
     * @param signature Signature byte array associated with _data
     */
    function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue);
}
合同源代码
文件 56 的 124:IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC165.sol)

pragma solidity ^0.8.20;

import {IERC165} from "../utils/introspection/IERC165.sol";
合同源代码
文件 57 的 124:IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the value of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the value of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 value) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the
     * allowance mechanism. `value` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 value) external returns (bool);
}
合同源代码
文件 58 的 124:IERC20Permit.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 *
 * ==== Security Considerations
 *
 * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
 * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
 * considered as an intention to spend the allowance in any specific way. The second is that because permits have
 * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
 * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
 * generally recommended is:
 *
 * ```solidity
 * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
 *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
 *     doThing(..., value);
 * }
 *
 * function doThing(..., uint256 value) public {
 *     token.safeTransferFrom(msg.sender, address(this), value);
 *     ...
 * }
 * ```
 *
 * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
 * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
 * {SafeERC20-safeTransferFrom}).
 *
 * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
 * contracts should have entry points that don't rely on permit.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     *
     * CAUTION: See Security Considerations above.
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @dev Returns the current nonce for `owner`. This value must be
     * included whenever a signature is generated for {permit}.
     *
     * Every successful call to {permit} increases ``owner``'s nonce by one. This
     * prevents a signature from being used multiple times.
     */
    function nonces(address owner) external view returns (uint256);

    /**
     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}
合同源代码
文件 59 的 124:IERC2981.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC2981.sol)

pragma solidity ^0.8.20;

import {IERC165} from "../utils/introspection/IERC165.sol";

/**
 * @dev Interface for the NFT Royalty Standard.
 *
 * A standardized way to retrieve royalty payment information for non-fungible tokens (NFTs) to enable universal
 * support for royalty payments across all NFT marketplaces and ecosystem participants.
 */
interface IERC2981 is IERC165 {
    /**
     * @dev Returns how much royalty is owed and to whom, based on a sale price that may be denominated in any unit of
     * exchange. The royalty amount is denominated and should be paid in that same unit of exchange.
     */
    function royaltyInfo(
        uint256 tokenId,
        uint256 salePrice
    ) external view returns (address receiver, uint256 royaltyAmount);
}
合同源代码
文件 60 的 124:IERC4906.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

/// @title EIP-721 Metadata Update Extension
interface IERC4906 {
    /// @dev This event emits when the metadata of a token is changed.
    /// So that the third-party platforms such as NFT market could
    /// timely update the images and related attributes of the NFT.
    event MetadataUpdate(uint256 _tokenId);

    /// @dev This event emits when the metadata of a range of tokens is changed.
    /// So that the third-party platforms such as NFT market could
    /// timely update the images and related attributes of the NFTs.
    event BatchMetadataUpdate(uint256 _fromTokenId, uint256 _toTokenId);
}
合同源代码
文件 61 的 124:IERC5267.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC5267.sol)

pragma solidity ^0.8.20;

interface IERC5267 {
    /**
     * @dev MAY be emitted to signal that the domain could have changed.
     */
    event EIP712DomainChanged();

    /**
     * @dev returns the fields and values that describe the domain separator used by this contract for EIP-712
     * signature.
     */
    function eip712Domain()
        external
        view
        returns (
            bytes1 fields,
            string memory name,
            string memory version,
            uint256 chainId,
            address verifyingContract,
            bytes32 salt,
            uint256[] memory extensions
        );
}
合同源代码
文件 62 的 124:IERC6551Registry.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

interface IERC6551Registry {
    /**
     * @dev The registry MUST emit the ERC6551AccountCreated event upon successful account creation.
     */
    event ERC6551AccountCreated(
        address account,
        address indexed implementation,
        bytes32 salt,
        uint256 chainId,
        address indexed tokenContract,
        uint256 indexed tokenId
    );

    /**
     * @dev The registry MUST revert with AccountCreationFailed error if the create2 operation fails.
     */
    error AccountCreationFailed();

    /**
     * @dev Creates a token bound account for a non-fungible token.
     *
     * If account has already been created, returns the account address without calling create2.
     *
     * Emits ERC6551AccountCreated event.
     *
     * @return account The address of the token bound account
     */
    function createAccount(
        address implementation,
        bytes32 salt,
        uint256 chainId,
        address tokenContract,
        uint256 tokenId
    ) external returns (address account);

    /**
     * @dev Returns the computed token bound account address for a non-fungible token.
     *
     * @return account The address of the token bound account
     */
    function account(
        address implementation,
        bytes32 salt,
        uint256 chainId,
        address tokenContract,
        uint256 tokenId
    ) external view returns (address account);
}
合同源代码
文件 63 的 124:IERC721.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/IERC721.sol)

pragma solidity ^0.8.20;

import {IERC165} from "../../utils/introspection/IERC165.sol";

/**
 * @dev Required interface of an ERC721 compliant contract.
 */
interface IERC721 is IERC165 {
    /**
     * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
     */
    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
     */
    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
     */
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    /**
     * @dev Returns the number of tokens in ``owner``'s account.
     */
    function balanceOf(address owner) external view returns (uint256 balance);

    /**
     * @dev Returns the owner of the `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function ownerOf(uint256 tokenId) external view returns (address owner);

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
     *   a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
     * are aware of the ERC721 protocol to prevent tokens from being forever locked.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or
     *   {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
     *   a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId) external;

    /**
     * @dev Transfers `tokenId` token from `from` to `to`.
     *
     * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721
     * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
     * understand this adds an external call which potentially creates a reentrancy vulnerability.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 tokenId) external;

    /**
     * @dev Gives permission to `to` to transfer `tokenId` token to another account.
     * The approval is cleared when the token is transferred.
     *
     * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
     *
     * Requirements:
     *
     * - The caller must own the token or be an approved operator.
     * - `tokenId` must exist.
     *
     * Emits an {Approval} event.
     */
    function approve(address to, uint256 tokenId) external;

    /**
     * @dev Approve or remove `operator` as an operator for the caller.
     * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
     *
     * Requirements:
     *
     * - The `operator` cannot be the address zero.
     *
     * Emits an {ApprovalForAll} event.
     */
    function setApprovalForAll(address operator, bool approved) external;

    /**
     * @dev Returns the account approved for `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function getApproved(uint256 tokenId) external view returns (address operator);

    /**
     * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
     *
     * See {setApprovalForAll}
     */
    function isApprovedForAll(address owner, address operator) external view returns (bool);
}
合同源代码
文件 64 的 124:IERC721Burnable.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

import {IERC721} from '@openzeppelin/contracts/token/ERC721/IERC721.sol';

/**
 * @title IERC721Burnable
 * @author OpenSocial Protocol
 * @dev This is the interface for all ERC721Burnable contracts.
 */
interface IERC721Burnable is IERC721 {
    /**
     * @dev Burns an NFT, removing it from circulation and essentially destroying it. This function can only
     * be called by the NFT to burn's owner.
     *
     * @param tokenId The token ID of the token to burn.
     */
    function burn(uint256 tokenId) external;
}
合同源代码
文件 65 的 124:IERC721Enumerable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/extensions/IERC721Enumerable.sol)

pragma solidity ^0.8.20;

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

/**
 * @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
 * @dev See https://eips.ethereum.org/EIPS/eip-721
 */
interface IERC721Enumerable is IERC721 {
    /**
     * @dev Returns the total amount of tokens stored by the contract.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns a token ID owned by `owner` at a given `index` of its token list.
     * Use along with {balanceOf} to enumerate all of ``owner``'s tokens.
     */
    function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256);

    /**
     * @dev Returns a token ID at a given `index` of all the tokens stored by the contract.
     * Use along with {totalSupply} to enumerate all tokens.
     */
    function tokenByIndex(uint256 index) external view returns (uint256);
}
合同源代码
文件 66 的 124:IERC721Metadata.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/extensions/IERC721Metadata.sol)

pragma solidity ^0.8.20;

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

/**
 * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
 * @dev See https://eips.ethereum.org/EIPS/eip-721
 */
interface IERC721Metadata is IERC721 {
    /**
     * @dev Returns the token collection name.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the token collection symbol.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
     */
    function tokenURI(uint256 tokenId) external view returns (string memory);
}
合同源代码
文件 67 的 124:IERC721Receiver.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/IERC721Receiver.sol)

pragma solidity ^0.8.20;

/**
 * @title ERC721 token receiver interface
 * @dev Interface for any contract that wants to support safeTransfers
 * from ERC721 asset contracts.
 */
interface IERC721Receiver {
    /**
     * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
     * by `operator` from `from`, this function is called.
     *
     * It must return its Solidity selector to confirm the token transfer.
     * If any other value is returned or the interface is not implemented by the recipient, the transfer will be
     * reverted.
     *
     * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
     */
    function onERC721Received(
        address operator,
        address from,
        uint256 tokenId,
        bytes calldata data
    ) external returns (bytes4);
}
合同源代码
文件 68 的 124:IFollowCondition.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

/**
 * @title IFollowCondition
 * @author OpenSocial Protocol
 *
 * @dev This is the standard interface for all OSP-compatible FollowConditions.
 */
interface IFollowCondition {
    /**
     * @dev Initializes a follow Condition for a given OSP profile. This can only be called by the osp contract.
     *
     * @param profileId The token ID of the profile to initialize this follow condition for.
     * @param data initialization data.
     */
    function initializeFollowCondition(uint256 profileId, bytes calldata data) external;

    /**
     * @dev Processes a given follow, this can only be called from the OSP contract.
     *
     * @param follower The follower address.
     * @param profileId The token ID of the profile being followed.
     * @param data data for processing.
     */
    function processFollow(
        address follower,
        uint256 profileId,
        bytes calldata data
    ) external payable;
}
合同源代码
文件 69 的 124:IFollowSBT.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

/**
 * @title IFollowSBT
 * @author OpenSocial Protocol
 *
 * @dev This is the interface for the FollowSBT contract, which is cloned upon the first follow for any profile.
 */
interface IFollowSBT {
    /**
     * @dev Initializes the follow NFT, setting the osp as the privileged minter and storing the associated profile ID.
     *
     * @param profileId The token ID of the profile in the osp associated with this FollowSBT, used for transfer hooks.
     * @param name The name to set for this NFT.
     * @param symbol The symbol to set for this NFT.
     */
    function initialize(uint256 profileId, string calldata name, string calldata symbol) external;

    /**
     * @dev Mints a follow NFT to the specified address. This can only be called by the osp, and is called
     * upon follow.
     *
     * @param to The address to mint the NFT to.
     *
     * @return uint256 An interger representing the minted token ID.
     */
    function mint(address to) external returns (uint256);
}
合同源代码
文件 70 的 124:IGovernanceLogic.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

import '../../../libraries/OspDataTypes.sol';
import '../../base/IERC4906.sol';
import '@openzeppelin/contracts/interfaces/IERC5267.sol';
import '../../../libraries/Constants.sol';
import '@openzeppelin/contracts/access/IAccessControl.sol';
import '@thirdweb-dev/contracts/extension/interface/IContractMetadata.sol';

/**
 * @title IGovernanceLogic
 * @author OpenSocial Protocol
 *
 * @dev This is the interface for the GovernanceLogic contract.
 */
interface IGovernanceLogic is IERC4906, IERC5267, IAccessControl, IContractMetadata {
    /**
     * @dev Initializes the Profile SBT, setting the initial governance address as well as the name and symbol.
     *
     * @param name The name to set for the osp NFT.
     * @param symbol The symbol to set for the osp NFT.
     */
    function initialize(
        string calldata name,
        string calldata symbol,
        address followSBTImpl,
        address joinNFTImpl,
        address communityNFT
    ) external;

    /**
     * @dev Sets the protocol state to either a global pause, a publishing pause or an unpaused state. This function
     * can only be called by the governance address or the emergency admin address.
     *
     * Note that this reverts if the emergency admin calls it if:
     *      1. The emergency admin is attempting to unpause.
     *      2. The emergency admin is calling while the protocol is already paused.
     *
     * @param newState The state to set, as a member of the ProtocolState enum.
     */
    function setState(OspDataTypes.ProtocolState newState) external;

    /**
     * @dev Adds or removes a app from the whitelist. This function can only be called by the
     * current governance address.
     *
     * @param app Reaction about the activity.
     * @param whitelist Whether or not the reaction should be whitelisted.
     */
    function whitelistApp(address app, bool whitelist) external;

    /**
     * @dev Adds or removes a ERC20 token from the whitelist. This function can only be called by the current
     * governance address.
     *
     * @param token ERC20 token address
     * @param whitelist whether or not the token should be whitelisted
     */
    function whitelistToken(address token, bool whitelist) external;

    /**
     * @dev Adds or removes a reserve community handle.This function can only be called by the current governance address.
     *
     * @param handle The handle to reserve.
     * @param isReserve Reserve or not the handle should be reserved.
     */
    function reserveCommunityHandle(string calldata handle, bool isReserve) external;

    /**
     * @dev Sets the base URI for NFTs. This function can only be called by the current governance
     * address.
     *
     * @param baseURI The base URI to set.
     */
    function setBaseURI(string calldata baseURI) external;

    function setERC6551AccountImpl(address accountImpl) external;

    function setJoinNFTImpl(address joinNFTImpl) external;

    /// ************************
    /// *****VIEW FUNCTIONS*****
    /// ************************

    function isAppWhitelisted(address app) external view returns (bool);

    /**
     * @dev Returns whether or not a token is whitelisted.
     *
     * @param token The address of the token to check.
     *
     * @return bool True if the the token whitelisted, false otherwise.
     */
    function isTokenWhitelisted(address token) external view returns (bool);

    /**
     * @dev Returns whether or not a community handle is reserved.
     *
     * @param handle The handle to check.
     *
     * @return bool True if the the handle is reserved, false otherwise.
     */
    function isReserveCommunityHandle(string calldata handle) external view returns (bool);

    /**
     * @dev Returns the base URI for the NFTs.
     *
     * @return string The base URI for the NFTs.
     */
    function getBaseURI() external view returns (string memory);

    /**
     * @dev Returns the follow NFT implementation address.
     *
     * @return address The follow NFT implementation address.
     */
    function getFollowSBTImpl() external view returns (address);

    /**
     * @dev Returns the join NFT implementation address.
     *
     * @return address The join NFT implementation address.
     */
    function getJoinNFTImpl() external view returns (address);

    /**
     * @dev Returns the community NFT address.
     *
     * @return address The community NFT address.
     */
    function getCommunityNFT() external view returns (address);

    function getERC6551AccountImpl() external view returns (address);

    /**
     * @dev Returns the current protocol state.
     *
     * @return ProtocolState The Protocol state, an enum, where:
     *      0: Unpaused
     *      1: PublishingPaused
     *      2: Paused
     */
    function getState() external view returns (OspDataTypes.ProtocolState);

    function updateMetadata() external;

    /**
     * @dev Update The treasure address.This function can only be called by the current governance.
     */
    function setTreasureAddress(address treasure) external;

    /**
     * @dev Get The treasure address.
     */
    function getTreasureAddress() external view returns (address);

    /**
     * @dev Set the royalty fee for the join NFT.
     */
    function setJoinNFTRoyalty(uint128 royaltyFraction, uint128 ospTreasureFraction) external;

    function joinNFTRoyaltyInfo()
        external
        view
        returns (uint128 royaltyFraction, uint128 ospTreasureFraction);
}
合同源代码
文件 71 的 124:IHoldERC721ActivityCond.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

import {IActivityCondition} from 'contracts/interfaces/IActivityCondition.sol';

/**
 * @title IHoldERC721ActivityCond
 * @dev You can only post if you have a specified ERC721 token.
 */
interface IHoldERC721ActivityCond is IActivityCondition {
    /**
     * @dev Struct to hold the data for the HoldERC721ActivityCond contract.
     * @param tokenAddress The address of the ERC721 token.
     * @param amount The min amount of the ERC721 token required.
     */
    struct HoldERC721Data {
        address tokenAddress;
        uint96 amount;
    }

    /**
     * @dev Returns the HoldERC721Data struct for the specified communityId.
     * @param communityId The ID of the community.
     * @return The HoldERC721Data struct.
     */
    function getHoldERC721Data(uint256 communityId) external view returns (HoldERC721Data memory);
}
合同源代码
文件 72 的 124:IHoldERC721ViewPrivacyCond.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

import {IViewPrivacyCondition} from 'contracts/interfaces/IViewPrivacyCondition.sol';

/**
 * @title IHoldERC721ViewPrivacyCond
 * @dev You can only view privacy if you have a specified ERC721 token.
 */
interface IHoldERC721ViewPrivacyCond is IViewPrivacyCondition {
    /**
     * @dev Struct to hold the data for the HoldERC721ActivityCond contract.
     * @param tokenAddress The address of the ERC721 token.
     * @param amount The amount of the ERC721 token required.
     */
    struct HoldERC721Data {
        address tokenAddress;
        uint96 amount;
    }

    /**
     * @dev Returns the HoldERC721Data struct for the specified communityId.
     * @param communityId The ID of the community.
     * @return The HoldERC721Data struct.
     */
    function getHoldERC721Data(uint256 communityId) external view returns (HoldERC721Data memory);
}
合同源代码
文件 73 的 124:IJoinCondition.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

/**
 * @title IJoinCondition
 * @author Osp Protocol
 *
 * @dev This is the standard interface for all Osp-compatible JoinConditions.
 */
interface IJoinCondition {
    /**
     * @dev Initializes a join Condition for a given OSP community. This can only be called by the osp contract.
     *
     * @param communityId The token ID of the community to initialize this join condition for.
     * @param data initialization data.
     */
    function initializeCommunityJoinCondition(uint256 communityId, bytes calldata data) external;

    /**
     * @dev Processes a given join, this can only be called from the OSP contract.
     *
     * @param joiner The joiner address.
     * @param community The token ID of the community being joined.
     * @param data data for processing.
     */
    function processJoin(address joiner, uint256 community, bytes calldata data) external payable;

    function processTransferJoinNFT(
        uint256 communityId,
        uint256 joinNFTId,
        address from,
        address to
    ) external;
}
合同源代码
文件 74 的 124:IJoinNFT.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

/**
 * @title IJoinNFT
 * @author OpenSocial Protocol
 *
 * @dev This is the interface for the JoinNFT contract. Deploy this NFT contract when creating the community
 */
interface IJoinNFT {
    /**
     * @dev Initializes the Join NFT, setting the feed as the privileged minter, storing the collected content pointer
     * and initializing the name and symbol in the OspNFTBase contract.
     *
     * @param communityId The community unique ID  this JoinNFT points to.
     * @param name The name to set for this NFT.
     * @param symbol The symbol to set for this NFT.
     */
    function initialize(uint256 communityId, string calldata name, string calldata symbol) external;

    /**
     * @dev Mints a join NFT to the specified address. This can only be called by the osp, and is called
     * upon collection.
     *
     * @param to The address to mint the NFT to.
     *
     * @return uint256 An interger representing the minted token ID.
     */
    function mint(address to) external returns (uint256);

    /**
     * @dev Sets the admin role for the specified account.
     *
     * @param account The account to set the admin role for.
     *
     * @return bool A boolean indicating whether the operation was successful.
     */
    function setAdmin(address account) external returns (bool);

    /**
     * @dev Sets the mods role for the specified account.
     *
     * @param account The account to set the mods role for.
     *
     * @return bool A boolean indicating whether the operation was successful.
     */
    function setModerator(address account) external returns (bool);

    /**
     * @dev Removes the specified role from the specified account.
     *
     * @param account The account to remove the role.
     *
     * @return bool A boolean indicating whether the operation was successful.
     */
    function removeRole(address account) external returns (bool);

    /**
     * @dev Sets the member level for the specified account.
     *
     * @param account The address to set the member level for.
     * @param level The level to set for the account.
     *
     * @return bool A boolean indicating whether the operation was successful.
     */
    function setMemberLevel(address account, uint256 level) external returns (bool);

    /**
     * @dev Adds or removes the specified account from the block list.
     *
     * @param account The account to add or remove from the block list.
     * @param enable A boolean indicating whether to add or remove the account from the block list.
     *
     * @return bool A boolean indicating whether the operation was successful.
     */
    function setBlockList(address account, bool enable) external returns (bool);

    /**
     * @dev Returns the source community pointer mapped to this collect NFT.
     *
     * @return communityId.
     */
    function getSourceCommunityPointer() external view returns (uint256);

    /**
     * @dev Returns `true` if `account` has been granted the `role`.
     */
    function hasRole(uint256 roles, address account) external view returns (bool);

    /**
     * @dev Returns the role of the account.
     */
    function getRole(address account) external view returns (uint256);

    /**
     * @dev Returns the member level of the account.
     */
    function getMemberLevel(address account) external view returns (uint256);

    /**
     * @dev Returns the member level of the tokenId.
     */
    function getMemberLevel(uint256 tokenId) external view returns (uint256);

    /**
     * @dev Returns the block status of the account.
     */
    function isBlock(address account) external view returns (bool);
}
合同源代码
文件 75 的 124:IOpenReaction.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

interface IOpenReaction {
    function processReaction(
        uint256 profileId,
        uint256 referencedProfileId,
        uint256 referencedContentId,
        bytes calldata data
    ) external payable;
}
合同源代码
文件 76 的 124:IProfileLogic.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

import '../../../libraries/OspDataTypes.sol';
import '../../../interfaces/IERC721Burnable.sol';
import {IERC721Metadata} from '@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol';
import {IERC721Enumerable} from '@openzeppelin/contracts/token/ERC721/extensions/IERC721Enumerable.sol';
import {IERC165} from '@openzeppelin/contracts/utils/introspection/IERC165.sol';

/**
 * @title IProfileLogic
 * @author OpenSocial Protocol
 *
 * @dev This is the interface for the ProfileLogic contract.
 */
interface IProfileLogic is IERC721Burnable, IERC721Metadata, IERC721Enumerable {
    /**
     * @dev Create a Profile for the message sender,each address can only be created once。
     *
     * @param vars A CreateProfileData struct containing the following params:
     *      handle: The handle to set for the profile, must be unique and non-empty,can only consist of lowercase letters, numbers and underscores
     *      followCondition: The follow condition to use, can be the zero address.
     *      followConditionInitData: The follow condition initialization data, if any.
     *
     * @return uint256 The token ID of the created profile.
     */
    function createProfile(OspDataTypes.CreateProfileData calldata vars) external returns (uint256);

    /**
     * @dev Sets a profile's follow condition, must be called by the profile owner.
     *
     * @param profileId The token ID of the profile to set the follow condition for.
     * @param followConditionInitCode The data to be passed to the follow condition for initialization.
     */
    function setFollowCondition(uint256 profileId, bytes calldata followConditionInitCode) external;

    /// ************************
    /// *****VIEW FUNCTIONS*****
    /// ************************

    /**
     * @dev Returns the FollowSBT of the profile.
     *
     * @param profileId The profile id to query.
     *
     * @return address Profile's FollowSBT address.
     */
    function getFollowSBT(uint256 profileId) external view returns (address);

    /**
     * @dev Returns the follow condition of the profile.
     *
     * @param profileId The profile id to query.
     *
     * @return address Profile's follow condition address.
     */
    function getFollowCondition(uint256 profileId) external view returns (address);

    /**
     * @dev Returns the handle of the profile.
     *
     * @param profileId The profile id to query.
     *
     * @return string  Profile's handle.
     */
    function getHandle(uint256 profileId) external view returns (string memory);

    /**
     * @dev Returns the profile ID according to a given handle.
     *
     * @param handle The handle to query.
     *
     * @return uint256 Profile ID the passed handle points to.
     */
    function getProfileIdByHandle(string calldata handle) external view returns (uint256);

    /**
     * @dev Returns the profile struct of the given profile ID.
     *
     * @param profileId The profile ID to query.
     *
     * @return ProfileStruct The profile struct of the given profile ID.
     */
    function getProfile(
        uint256 profileId
    ) external view returns (OspDataTypes.ProfileStruct memory);

    /**
     * @dev Returns the profile id according to a given address. if not found returns 0.
     *
     * @param addr The address to query.
     *
     * @return uint256 Profile ID associated with the address
     */
    function getProfileIdByAddress(address addr) external view returns (uint256);

    function nonces(address singer) external view returns (uint256);
}
合同源代码
文件 77 的 124:IReferenceCondition.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

/**
 * @title IReferenceCondition
 * @author Osp Protocol
 *
 * @dev This is the standard interface for all Osp-compatible ReferenceConditions.
 */
interface IReferenceCondition {
    function initializeReferenceCondition(
        uint256 profileId,
        uint256 contendId,
        uint256 communityId,
        bytes calldata data
    ) external;

    function processReactionReference(
        uint256 profileId,
        uint256 communityId,
        uint256 referencedProfileId,
        uint256 referencedContentId,
        bytes calldata data
    ) external payable;
}
合同源代码
文件 78 的 124:IRelationLogic.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

import '../../../libraries/OspDataTypes.sol';

/**
 * @title IReactionLogic
 * @author OpenSocial Protocol
 *
 * @dev This is the interface for the ReactionLogic contract.
 */
interface IRelationLogic {
    /**
     * @dev Follow a profile, executing the profile's follow condition logic (if any) and minting followSBTs to the caller.
     *
     * @return uint256 An integer representing the minted follow SBTs token ID.
     */
    function follow(OspDataTypes.FollowData calldata vars) external payable returns (uint256);

    /**
     * @dev Follow a profile, executing the profile's follow condition logic (if any) and minting followSBTs to the caller.
     *  NOTE: Both the `profileIds` and `datas` arrays must be of the same length, regardless if the profiles do not have a follow condition set.
     *
     * @return uint256 An integer representing the minted follow SBTs token ID.
     */
    function batchFollow(
        OspDataTypes.BatchFollowData calldata vars
    ) external payable returns (uint256[] memory);

    /**
     * @dev Join a community, executing the community's join condition logic (if any) and minting joinNFTs to the caller.
     *
     * @return uint256 An integer representing the minted join NFTs token ID.
     */
    function join(OspDataTypes.JoinData calldata vars) external payable returns (uint256);

    /**
     * @dev Join a community, executing the community's join condition logic (if any) and minting joinNFTs to the caller.
     *  NOTE: Both the `communityIds` and `datas` arrays must be of the same length, regardless if the communities do not have a join condition set.
     *
     * @return uint256 An integer representing the minted join NFTs token ID.
     */
    function batchJoin(
        OspDataTypes.BatchJoinData calldata vars
    ) external payable returns (uint256[] memory);

    /**
     * @dev Returns the URI for a followSBT token.
     *
     * @param profileId  The token ID of the profile associated with the follow SBT.
     * @param tokenId  The followSBTId being transferred's token ID.
     */
    function getFollowSBTURI(
        uint256 profileId,
        uint256 tokenId
    ) external view returns (string memory);

    /**
     * @dev Returns addr is follower for a gave profile.
     *
     * @param profileId The profileID to query.
     * @param addr The address of the follower.
     */
    function isFollow(uint256 profileId, address addr) external view returns (bool);

    /**
     * @dev Returns addr is member for a gave community.
     *
     * @param communityId The communityID to query.
     * @param addr The address of the member.
     */
    function isJoin(uint256 communityId, address addr) external view returns (bool);

    /**
     * @dev Returns `true` if the account has the specified role in the community.
     *
     * @param communityId The community ID to check the role for.
     * @param role The role to check for.
     * @param account The account to check the role for.
     */
    function hasCommunityRole(
        uint256 communityId,
        uint256 role,
        address account
    ) external view returns (bool);

    /**
     * @dev Returns the role of the account in the community.
     *
     * @param communityId The community ID to get the role.
     * @param account The account to get role for.
     */
    function getCommunityRole(uint256 communityId, address account) external view returns (uint256);

    /**
     * @dev Returns the level of the account in the community.
     *
     * @param communityId The community ID to get the level for.
     * @param account The account to get level for.
     */
    function getCommunityMemberLevel(
        uint256 communityId,
        address account
    ) external view returns (uint256);

    /**
     * @dev Returns the block status of the account in the community.
     *
     * @param communityId The community ID to get the block status for.
     * @param account The account to get block status for.
     */
    function isCommunityBlock(uint256 communityId, address account) external view returns (bool);

    /**
     * @dev Returns the URI for a joinNFT token.
     *
     * @param communityId  The communityId associated with the join NFT.
     * @param tokenId  The joinNFTId being transferred's token ID.
     */
    function getJoinNFTURI(
        uint256 communityId,
        uint256 tokenId
    ) external view returns (string memory);

    /**
     *  @dev emit FollowSBTTransaction event to facilitate the monitoring of events.
     *
     * @param profileId The token ID of the profile associated with the follow SBT being transferred.
     * @param followSBTId The followSBTId being transferred's token ID.
     * @param from The address the collectNFT is being transferred from.
     * @param to The address the collectNFT is being transferred to.
     */
    function emitFollowSBTTransferEvent(
        uint256 profileId,
        uint256 followSBTId,
        address from,
        address to
    ) external;

    /**
     * @dev emit JoinNFTTransaction event to facilitate the monitoring of events.
     *
     * @param communityId The communityId associated with the join NFT being transferred.
     * @param joinNFTId The joinNFTId being transferred's token ID.
     * @param from The address the joinNFT is being transferred from.
     * @param to The address the joinNFT is being transferred to.
     */
    function emitJoinNFTTransferEvent(
        uint256 communityId,
        uint256 joinNFTId,
        address from,
        address to
    ) external;

    function emitJoinNFTRoleChangedEvent(
        uint256 communityId,
        address sender,
        address account,
        uint256 role
    ) external;

    function emitJoinNFTAccountBlockedEvent(
        uint256 communityId,
        address sender,
        address account,
        bool isBlock
    ) external;

    function emitJoinNFTAccountLevelChangedEvent(
        uint256 communityId,
        address sender,
        uint256 tokenId,
        address account,
        uint256 level
    ) external;
}
合同源代码
文件 79 的 124:IRoleActivityCond.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

import {IActivityCondition} from 'contracts/interfaces/IActivityCondition.sol';

/**
 * @title IRoleActivityCond
 * @dev Have a community role to post
 */
interface IRoleActivityCond is IActivityCondition {
    /**
     * @dev Returns the role limit for a given community.
     * @param communityId The ID of the community.
     * @return roleLimit The role limit for the community.
     */
    function getRoleLimit(uint256 communityId) external view returns (uint256 roleLimit);
}
合同源代码
文件 80 的 124:IRoleViewPrivacyCond.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

import {IViewPrivacyCondition} from 'contracts/interfaces/IViewPrivacyCondition.sol';

/**
 * @title IRoleViewPrivacyCond
 * @dev Have a community role to view privacy
 */
interface IRoleViewPrivacyCond is IViewPrivacyCondition {
    /**
     * @dev Struct to store role limit data.
     */
    struct RoleLimitData {
        uint256 roleLimit;
    }

    /**
     * @dev Returns the role limit for a given community.
     * @param communityId The ID of the community.
     * @return roleLimit The role limit for the community.
     */
    function getRoleLimit(uint256 communityId) external view returns (uint256 roleLimit);
}
合同源代码
文件 81 的 124:IRouter.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

interface IRouter {
    /**
     *  @dev An interface to describe a plug-in.
     *
     *  @param functionSelector     4-byte function selector.
     *  @param functionSignature    Function representation as a string. E.g. "transfer(address,address,uint256)"
     *  @param routerAddress        Address of the contract containing the function.
     */
    struct Router {
        bytes4 functionSelector;
        address routerAddress;
        string functionSignature;
    }

    /// @dev Emitted when a functionality is added, or plugged-in.
    event RouterAdded(bytes4 indexed functionSelector, address indexed routerAddress);

    /// @dev Emitted when a functionality is updated or overridden.
    event RouterUpdated(
        bytes4 indexed functionSelector,
        address indexed oldRouterAddress,
        address indexed newRouterAddress
    );

    /// @dev Emitted when a function selector is mapped to a particular plug-in smart contract, during construction of Map.
    event RouterSet(
        bytes4 indexed functionSelector,
        string indexed functionSignature,
        address indexed routerAddress
    );

    /// @dev Emitted when a functionality is removed.
    event RouterRemoved(bytes4 indexed functionSelector, address indexed routerAddress);

    /// @dev Add a new router to the contract.
    function addRouter(Router memory router) external;

    /// @dev Update / override an existing router.
    function updateRouter(Router memory router) external;

    /// @dev Remove an existing router from the contract.
    function removeRouter(bytes4 selector, string calldata functionSignature) external;

    /// @dev Returns the plug-in contract for a given function.
    function getRouterForFunction(bytes4 functionSelector) external view returns (address);

    /// @dev Returns all functions that are mapped to the given plug-in contract.
    function getAllFunctionsOfRouter(address routerAddress) external view returns (bytes4[] memory);

    /// @dev Returns all plug-ins known by Map.
    function getAllRouters() external view returns (Router[] memory);
}
合同源代码
文件 82 的 124:IViewPrivacyCondition.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

/**
 * @title IViewPrivacyCondition
 * @author OpenSocial Protocol
 *
 * @dev This interface is used to define who can view the privacy content.
 */
interface IViewPrivacyCondition {
    /**
     * @dev Initializes the view privacy condition for a community.
     *
     * @param communityId The ID of the community.
     * @param data The data to initialize the view privacy condition.
     */
    function initializeCommunityViewPrivacyCondition(
        uint256 communityId,
        bytes calldata data
    ) external;

    /**
     * @dev Checks if a profile can view the privacy content.
     *
     * @param communityId The ID of the community.
     * @param profileOwner The owner of the profile.
     * @param profileId The ID of the profile.
     *
     * @return A boolean indicating if the profile can view the privacy content.
     */
    function isViewPrivacyAllowed(
        uint256 communityId,
        address profileOwner,
        uint256 profileId
    ) external view returns (bool);
}
合同源代码
文件 83 的 124:Initializable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol)

pragma solidity ^0.8.20;

/**
 * @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 proxied contracts do not make use of 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.
 *
 * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
 * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
 * case an upgrade adds a module that needs to be initialized.
 *
 * For example:
 *
 * [.hljs-theme-light.nopadding]
 * ```solidity
 * contract MyToken is ERC20Upgradeable {
 *     function initialize() initializer public {
 *         __ERC20_init("MyToken", "MTK");
 *     }
 * }
 *
 * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
 *     function initializeV2() reinitializer(2) public {
 *         __ERC20Permit_init("MyToken");
 *     }
 * }
 * ```
 *
 * 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 prevent the implementation contract from being used, you should invoke
 * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * /// @custom:oz-upgrades-unsafe-allow constructor
 * constructor() {
 *     _disableInitializers();
 * }
 * ```
 * ====
 */
abstract contract Initializable {
    /**
     * @dev Storage of the initializable contract.
     *
     * It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
     * when using with upgradeable contracts.
     *
     * @custom:storage-location erc7201:openzeppelin.storage.Initializable
     */
    struct InitializableStorage {
        /**
         * @dev Indicates that the contract has been initialized.
         */
        uint64 _initialized;
        /**
         * @dev Indicates that the contract is in the process of being initialized.
         */
        bool _initializing;
    }

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;

    /**
     * @dev The contract is already initialized.
     */
    error InvalidInitialization();

    /**
     * @dev The contract is not initializing.
     */
    error NotInitializing();

    /**
     * @dev Triggered when the contract has been initialized or reinitialized.
     */
    event Initialized(uint64 version);

    /**
     * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
     * `onlyInitializing` functions can be used to initialize parent contracts.
     *
     * Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
     * number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
     * production.
     *
     * Emits an {Initialized} event.
     */
    modifier initializer() {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        // Cache values to avoid duplicated sloads
        bool isTopLevelCall = !$._initializing;
        uint64 initialized = $._initialized;

        // Allowed calls:
        // - initialSetup: the contract is not in the initializing state and no previous version was
        //                 initialized
        // - construction: the contract is initialized at version 1 (no reininitialization) and the
        //                 current contract is just being deployed
        bool initialSetup = initialized == 0 && isTopLevelCall;
        bool construction = initialized == 1 && address(this).code.length == 0;

        if (!initialSetup && !construction) {
            revert InvalidInitialization();
        }
        $._initialized = 1;
        if (isTopLevelCall) {
            $._initializing = true;
        }
        _;
        if (isTopLevelCall) {
            $._initializing = false;
            emit Initialized(1);
        }
    }

    /**
     * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
     * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
     * used to initialize parent contracts.
     *
     * A reinitializer may be used after the original initialization step. This is essential to configure modules that
     * are added through upgrades and that require initialization.
     *
     * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
     * cannot be nested. If one is invoked in the context of another, execution will revert.
     *
     * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
     * a contract, executing them in the right order is up to the developer or operator.
     *
     * WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.
     *
     * Emits an {Initialized} event.
     */
    modifier reinitializer(uint64 version) {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        if ($._initializing || $._initialized >= version) {
            revert InvalidInitialization();
        }
        $._initialized = version;
        $._initializing = true;
        _;
        $._initializing = false;
        emit Initialized(version);
    }

    /**
     * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
     * {initializer} and {reinitializer} modifiers, directly or indirectly.
     */
    modifier onlyInitializing() {
        _checkInitializing();
        _;
    }

    /**
     * @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
     */
    function _checkInitializing() internal view virtual {
        if (!_isInitializing()) {
            revert NotInitializing();
        }
    }

    /**
     * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
     * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
     * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
     * through proxies.
     *
     * Emits an {Initialized} event the first time it is successfully executed.
     */
    function _disableInitializers() internal virtual {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        if ($._initializing) {
            revert InvalidInitialization();
        }
        if ($._initialized != type(uint64).max) {
            $._initialized = type(uint64).max;
            emit Initialized(type(uint64).max);
        }
    }

    /**
     * @dev Returns the highest version that has been initialized. See {reinitializer}.
     */
    function _getInitializedVersion() internal view returns (uint64) {
        return _getInitializableStorage()._initialized;
    }

    /**
     * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
     */
    function _isInitializing() internal view returns (bool) {
        return _getInitializableStorage()._initializing;
    }

    /**
     * @dev Returns a pointer to the storage namespace.
     */
    // solhint-disable-next-line var-name-mixedcase
    function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
        assembly {
            $.slot := INITIALIZABLE_STORAGE
        }
    }
}
合同源代码
文件 84 的 124:JoinConditionBase.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

import {OspContext} from './OspContext.sol';
import {IJoinCondition} from '../../interfaces/IJoinCondition.sol';
import {IERC165} from '@openzeppelin/contracts/interfaces/IERC165.sol';

abstract contract JoinConditionBase is OspContext, IERC165, IJoinCondition {
    constructor(address osp) OspContext(osp) {}

    function supportsInterface(bytes4 interfaceId) external pure override returns (bool) {
        return
            interfaceId == type(IJoinCondition).interfaceId ||
            interfaceId == type(IERC165).interfaceId;
    }

    function initializeCommunityJoinCondition(
        uint256 communityId,
        bytes calldata data
    ) external onlyOsp {
        _initializeCommunityJoinCondition(communityId, data);
    }

    function processJoin(
        address joiner,
        uint256 community,
        bytes calldata data
    ) external payable onlyOsp {
        _processJoin(joiner, community, data);
    }

    function processTransferJoinNFT(
        uint256 communityId,
        uint256 joinNFTId,
        address from,
        address to
    ) external onlyOsp {
        _processTransferJoinNFT(communityId, joinNFTId, from, to);
    }

    function _initializeCommunityJoinCondition(
        uint256 communityId,
        bytes calldata data
    ) internal virtual;

    function _processJoin(address joiner, uint256 community, bytes calldata data) internal virtual;

    function _processTransferJoinNFT(
        uint256 communityId,
        uint256 joinNFTId,
        address from,
        address to
    ) internal virtual {}
}
合同源代码
文件 85 的 124:JoinNFT.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

import {IJoinNFT} from '../interfaces/IJoinNFT.sol';
import {OspErrors} from '../libraries/OspErrors.sol';
import {OspEvents} from '../libraries/OspEvents.sol';
import {Constants} from '../libraries/Constants.sol';
import {OspDataTypes} from '../libraries/OspDataTypes.sol';
import {Payment} from '../libraries/Payment.sol';
import {OspNFTBase, ERC721Upgradeable} from './base/OspNFTBase.sol';
import {OspClient} from './logics/interfaces/OspClient.sol';

import {IERC721} from '@openzeppelin/contracts/token/ERC721/IERC721.sol';
import {IERC2981} from '@openzeppelin/contracts/interfaces/IERC2981.sol';
import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';

/**
 * @title JoinNFT
 * @author OpenSocial Protocol
 * @dev This is the NFT contract that is minted upon joining a community. It is cloned upon first community is created.
 */
contract JoinNFT is OspNFTBase, IJoinNFT, IERC2981 {
    using Payment for address;

    address public immutable OSP;

    uint256 internal _communityId;
    uint256 internal _tokenIdCounter;
    mapping(address => bool) internal _blockList;
    mapping(address => uint256) internal _role;
    mapping(uint256 => uint256) internal _level;

    modifier notBlock(address account) {
        if (_blockList[account]) revert OspErrors.JoinNFTBlocked();
        _;
    }

    // We create the CollectNFT with the pre-computed OSP address before deploying the osp proxy in order
    // to initialize the osp proxy at construction.
    constructor(address osp) {
        if (osp == address(0)) revert OspErrors.InitParamsInvalid();
        OSP = osp;
    }

    /// @inheritdoc IJoinNFT
    function initialize(
        uint256 communityId,
        string calldata name,
        string calldata symbol
    ) external override {
        if (msg.sender != OSP) revert OspErrors.NotOSP();
        _communityId = communityId;
        super._initialize(name, symbol);
        emit OspEvents.JoinNFTInitialized(communityId, block.timestamp);
    }

    /// @inheritdoc IJoinNFT
    function mint(address to) external override returns (uint256) {
        if (msg.sender != OSP) revert OspErrors.NotOSP();
        unchecked {
            uint256 tokenId = ++_tokenIdCounter;
            _mint(to, tokenId);
            return tokenId;
        }
    }

    /// @inheritdoc IJoinNFT
    function setAdmin(address account) public override notBlock(account) returns (bool) {
        return _setRole(Constants.COMMUNITY_ADMIN_ACCESS, account);
    }

    /// @inheritdoc IJoinNFT
    function setModerator(address account) public override notBlock(account) returns (bool) {
        return _setRole(Constants.COMMUNITY_MODERATOR_ACCESS, account);
    }

    /// @inheritdoc IJoinNFT
    function removeRole(address account) public override returns (bool) {
        return _setRole(Constants.COMMUNITY_NULL_ACCESS, account);
    }

    /// @inheritdoc IJoinNFT
    function setMemberLevel(address account, uint256 level) public override returns (bool) {
        uint256 tokenId = tokenOfOwnerByIndex(account, 0);
        if (hasRole(Constants.COMMUNITY_MODERATOR_ACCESS, _msgSender())) {
            if (_level[tokenId] != level) {
                _level[tokenId] = level;
                OspClient(OSP).emitJoinNFTAccountLevelChangedEvent(
                    _communityId,
                    _msgSender(),
                    tokenId,
                    ownerOf(tokenId),
                    level
                );
                return true;
            }
            return false;
        }
        revert OspErrors.JoinNFTUnauthorizedAccount();
    }

    /// @inheritdoc IJoinNFT
    function setBlockList(address account, bool enable) public override returns (bool) {
        if (hasRole(Constants.COMMUNITY_MODERATOR_ACCESS, _msgSender())) {
            if (_blockList[account] != enable) {
                _blockList[account] = enable;
                OspClient(OSP).emitJoinNFTAccountBlockedEvent(
                    _communityId,
                    _msgSender(),
                    account,
                    enable
                );
                return true;
            }
            return false;
        }
        revert OspErrors.JoinNFTUnauthorizedAccount();
    }

    function withdraw(address token) external {
        OspClient ospClient = OspClient(OSP);
        (, uint128 ospTreasureFraction) = ospClient.joinNFTRoyaltyInfo();
        address treasureAddress = ospClient.getTreasureAddress();
        address communityAccount = ospClient.getCommunityAccount(_communityId);

        uint256 value;
        if (address(0) == token) {
            value = address(this).balance;
            uint256 serviceFee = (value * ospTreasureFraction) / Constants.ROYALTY_DENOMINATOR;
            Payment.payNative(treasureAddress, serviceFee);
            Payment.payNative(communityAccount, value - serviceFee);
        } else {
            value = IERC20(token).balanceOf(address(this));
            uint256 serviceFee = (value * ospTreasureFraction) / Constants.ROYALTY_DENOMINATOR;
            token.payERC20(treasureAddress, serviceFee);
            token.payERC20(communityAccount, value - serviceFee);
        }
    }

    /// @inheritdoc IJoinNFT
    function getSourceCommunityPointer() external view override returns (uint256) {
        return _communityId;
    }

    function tokenURI(uint256 tokenId) public view override returns (string memory) {
        return OspClient(OSP).getJoinNFTURI(_communityId, tokenId);
    }

    function balanceOf(
        address addr
    ) public view override(IERC721, ERC721Upgradeable) notBlock(addr) returns (uint256) {
        return super.balanceOf(addr);
    }

    function ownerOf(
        uint256 tokenId
    ) public view override(IERC721, ERC721Upgradeable) returns (address) {
        address owner = super.ownerOf(tokenId);
        if (_blockList[owner]) {
            revert OspErrors.JoinNFTBlocked();
        }
        return owner;
    }

    /// @inheritdoc IJoinNFT
    function hasRole(uint256 roles, address account) public view override returns (bool) {
        return _role[account] >= roles || _isCommunityOwner(account);
    }

    /// @inheritdoc IJoinNFT
    function getRole(address account) public view override returns (uint256) {
        if (_isCommunityOwner(account)) {
            return type(uint256).max;
        }
        return _role[account];
    }

    /// @inheritdoc IJoinNFT
    function getMemberLevel(address account) external view override returns (uint256) {
        return _level[tokenOfOwnerByIndex(account, 0)];
    }

    /// @inheritdoc IJoinNFT
    function getMemberLevel(uint256 tokenId) external view override returns (uint256) {
        return _level[tokenId];
    }

    /// @inheritdoc IJoinNFT
    function isBlock(address account) external view override returns (bool) {
        return _blockList[account];
    }

    /// @inheritdoc IERC2981
    function royaltyInfo(
        uint256 /*tokenId*/,
        uint256 salePrice
    ) external view returns (address receiver, uint256 royaltyAmount) {
        (uint128 royaltyFraction, ) = OspClient(OSP).joinNFTRoyaltyInfo();
        return (address(this), (salePrice * royaltyFraction) / Constants.ROYALTY_DENOMINATOR);
    }

    /**
     * @dev Upon transfers, we emit the transfer event in the osp.
     */
    function _afterTokenTransfer(address from, address to, uint256 tokenId) internal override {
        if (_blockList[from] || _blockList[to]) {
            revert OspErrors.JoinNFTBlocked();
        }
        super._afterTokenTransfer(from, to, tokenId);
        if (to != address(0) && balanceOf(to) > 1) revert OspErrors.JoinNFTDuplicated();
        if (from != address(0)) {
            _setRole(Constants.COMMUNITY_NULL_ACCESS, from);
        }
        if (to == address(0)) {
            _level[tokenId] = 0;
        }
        OspClient(OSP).emitJoinNFTTransferEvent(_communityId, tokenId, from, to);
    }

    function _isCommunityOwner(address account) internal view returns (bool) {
        return IERC721(OspClient(OSP).getCommunityNFT()).ownerOf(_communityId) == account;
    }

    /**
     * @dev Grant a role to an account.
     */
    function _setRole(uint256 role, address account) internal returns (bool) {
        address sender = _msgSender();
        uint256 oldRole = getRole(account);

        if (balanceOf(account) == 0) {
            if (role != Constants.COMMUNITY_NULL_ACCESS) revert OspErrors.NotJoinCommunity();
        } else {
            uint256 senderRole = getRole(sender);
            if (senderRole <= oldRole || role >= senderRole) {
                revert OspErrors.JoinNFTUnauthorizedAccount();
            }
        }

        if (oldRole != role) {
            _role[account] = role;
            OspClient(OSP).emitJoinNFTRoleChangedEvent(_communityId, sender, account, role);
            return true;
        }
        return false;
    }
}
合同源代码
文件 86 的 124:JoinNFTProxy.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

import {IGovernanceLogic} from '../core/logics/interfaces/IGovernanceLogic.sol';
import {Proxy} from '@openzeppelin/contracts/proxy/Proxy.sol';
import {Address} from '@openzeppelin/contracts/utils/Address.sol';

contract JoinNFTProxy is Proxy {
    using Address for address;
    address immutable OSP;

    constructor(bytes memory data) {
        OSP = msg.sender;
        IGovernanceLogic(msg.sender).getJoinNFTImpl().functionDelegateCall(data);
    }

    function _implementation() internal view override returns (address) {
        return IGovernanceLogic(OSP).getJoinNFTImpl();
    }
}
合同源代码
文件 87 的 124:LikeReaction.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

import {OpenReactionBase} from '../base/OpenReactionBase.sol';
import {OspErrors} from '../../libraries/OspErrors.sol';

contract LikeReaction is OpenReactionBase {
    constructor(address osp) OpenReactionBase(osp) {}

    function _processReaction(
        uint256 /*profileId*/,
        uint256 /*referencedProfileId*/,
        uint256 /*referencedContentId*/,
        bytes calldata data
    ) internal view override {
        abi.decode(data, (bool));
    }
}
合同源代码
文件 88 的 124:Math.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)

pragma solidity ^0.8.20;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    /**
     * @dev Muldiv operation overflow.
     */
    error MathOverflowedMulDiv();

    enum Rounding {
        Floor, // Toward negative infinity
        Ceil, // Toward positive infinity
        Trunc, // Toward zero
        Expand // Away from zero
    }

    /**
     * @dev Returns the addition of two unsigned integers, with an overflow flag.
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            uint256 c = a + b;
            if (c < a) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, with an overflow flag.
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b > a) return (false, 0);
            return (true, a - b);
        }
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
            // benefit is lost if 'b' is also tested.
            // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
            if (a == 0) return (true, 0);
            uint256 c = a * b;
            if (c / a != b) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the division of two unsigned integers, with a division by zero flag.
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a / b);
        }
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a % b);
        }
    }

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

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

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

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds towards infinity instead
     * of rounding towards zero.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        if (b == 0) {
            // Guarantee the same behavior as in a regular Solidity division.
            return a / b;
        }

        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

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

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                // The surrounding unchecked block does not change this fact.
                // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            if (denominator <= prod1) {
                revert MathOverflowedMulDiv();
            }

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

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

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

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

            uint256 twos = denominator & (0 - denominator);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    /**
     * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
     */
    function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
        return uint8(rounding) % 2 == 1;
    }
}
合同源代码
文件 89 的 124:MessageHashUtils.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/MessageHashUtils.sol)

pragma solidity ^0.8.20;

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

/**
 * @dev Signature message hash utilities for producing digests to be consumed by {ECDSA} recovery or signing.
 *
 * The library provides methods for generating a hash of a message that conforms to the
 * https://eips.ethereum.org/EIPS/eip-191[EIP 191] and https://eips.ethereum.org/EIPS/eip-712[EIP 712]
 * specifications.
 */
library MessageHashUtils {
    /**
     * @dev Returns the keccak256 digest of an EIP-191 signed data with version
     * `0x45` (`personal_sign` messages).
     *
     * The digest is calculated by prefixing a bytes32 `messageHash` with
     * `"\x19Ethereum Signed Message:\n32"` and hashing the result. It corresponds with the
     * hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method.
     *
     * NOTE: The `messageHash` parameter is intended to be the result of hashing a raw message with
     * keccak256, although any bytes32 value can be safely used because the final digest will
     * be re-hashed.
     *
     * See {ECDSA-recover}.
     */
    function toEthSignedMessageHash(bytes32 messageHash) internal pure returns (bytes32 digest) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, "\x19Ethereum Signed Message:\n32") // 32 is the bytes-length of messageHash
            mstore(0x1c, messageHash) // 0x1c (28) is the length of the prefix
            digest := keccak256(0x00, 0x3c) // 0x3c is the length of the prefix (0x1c) + messageHash (0x20)
        }
    }

    /**
     * @dev Returns the keccak256 digest of an EIP-191 signed data with version
     * `0x45` (`personal_sign` messages).
     *
     * The digest is calculated by prefixing an arbitrary `message` with
     * `"\x19Ethereum Signed Message:\n" + len(message)` and hashing the result. It corresponds with the
     * hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method.
     *
     * See {ECDSA-recover}.
     */
    function toEthSignedMessageHash(bytes memory message) internal pure returns (bytes32) {
        return
            keccak256(bytes.concat("\x19Ethereum Signed Message:\n", bytes(Strings.toString(message.length)), message));
    }

    /**
     * @dev Returns the keccak256 digest of an EIP-191 signed data with version
     * `0x00` (data with intended validator).
     *
     * The digest is calculated by prefixing an arbitrary `data` with `"\x19\x00"` and the intended
     * `validator` address. Then hashing the result.
     *
     * See {ECDSA-recover}.
     */
    function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked(hex"19_00", validator, data));
    }

    /**
     * @dev Returns the keccak256 digest of an EIP-712 typed data (EIP-191 version `0x01`).
     *
     * The digest is calculated from a `domainSeparator` and a `structHash`, by prefixing them with
     * `\x19\x01` and hashing the result. It corresponds to the hash signed by the
     * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] JSON-RPC method as part of EIP-712.
     *
     * See {ECDSA-recover}.
     */
    function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 digest) {
        /// @solidity memory-safe-assembly
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, hex"19_01")
            mstore(add(ptr, 0x02), domainSeparator)
            mstore(add(ptr, 0x22), structHash)
            digest := keccak256(ptr, 0x42)
        }
    }
}
合同源代码
文件 90 的 124:Multicall.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Multicall.sol)

pragma solidity ^0.8.20;

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

/**
 * @dev Provides a function to batch together multiple calls in a single external call.
 */
abstract contract Multicall {
    /**
     * @dev Receives and executes a batch of function calls on this contract.
     * @custom:oz-upgrades-unsafe-allow-reachable delegatecall
     */
    function multicall(bytes[] calldata data) external virtual returns (bytes[] memory results) {
        results = new bytes[](data.length);
        for (uint256 i = 0; i < data.length; i++) {
            results[i] = Address.functionDelegateCall(address(this), data[i]);
        }
        return results;
    }
}
合同源代码
文件 91 的 124:NativeFeeJoinCond.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

import {CondErrors} from '../libraries/CondErrors.sol';
import {JoinConditionBase} from '../../base/JoinConditionBase.sol';
import {Payment} from '../../../libraries/Payment.sol';

/**
 * @title CommunityData
 * @param amount The amount of native tokens to charge for joining
 * @param recipient The address to send the native tokens to
 */
struct CommunityData {
    uint256 amount;
    address recipient;
}

/**
 * @title NativeFeeJoinCond
 * @author OpenSocial
 * @dev The condition allows users to join a community by paying a fee in native tokens.
 */
contract NativeFeeJoinCond is JoinConditionBase {
    mapping(uint256 => CommunityData) internal _dataByCommunity;

    constructor(address osp) JoinConditionBase(osp) {}

    /**
     * @dev Initializes the condition data for a given community.
     */
    function _initializeCommunityJoinCondition(
        uint256 communityId,
        bytes calldata data
    ) internal override onlyOsp {
        (uint256 amount, address recipient) = abi.decode(data, (uint256, address));
        if (recipient == address(0) || amount == 0) revert CondErrors.InitParamsInvalid();
        _dataByCommunity[communityId].amount = amount;
        _dataByCommunity[communityId].recipient = recipient;
    }

    /**
     * @dev process join,transfer native token to recipient.
     */
    function _processJoin(
        address follower,
        uint256 communityId,
        bytes calldata data
    ) internal override onlyOsp {
        uint256 value = msg.value;
        if (value != _dataByCommunity[communityId].amount)
            revert CondErrors.ConditionDataMismatch();
        Payment.payNative(_dataByCommunity[communityId].recipient, value);
        (follower, data); //unused
    }

    /**
     * @dev Returns the community data for a given community, or an empty struct if that community was not initialized
     * with this condition.
     */
    function getCommunityData(uint256 communityId) external view returns (CommunityData memory) {
        return _dataByCommunity[communityId];
    }
}
合同源代码
文件 92 的 124:OnlyMemberReferenceCond.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

import {OspErrors} from '../../../libraries/OspErrors.sol';
import {ReferenceConditionBase} from '../../base/ReferenceConditionBase.sol';

contract OnlyMemberReferenceCond is ReferenceConditionBase {
    constructor(address osp) ReferenceConditionBase(osp) {}

    function _initializeReferenceCondition(
        uint256 /*profileId*/,
        uint256 /*communityId*/,
        uint256 /*contendId*/,
        bytes calldata /*data*/
    ) internal override {
        // nothing to do
    }

    function _processReactionReference(
        uint256 /*profileId*/,
        uint256 communityId,
        uint256 referencedProfileId,
        uint256 referencedContentId,
        bytes calldata /*data*/
    ) internal override nonPayable {
        uint256 referencedCommunityId = OSP.getCommunityIdByContent(
            referencedProfileId,
            referencedContentId
        );
        if (communityId != referencedCommunityId) {
            revert OspErrors.InvalidCommunityId();
        }
    }
}
合同源代码
文件 93 的 124:OpenReactionBase.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

import {IOpenReaction} from '../../interfaces/IOpenReaction.sol';
import {OspContext} from './OspContext.sol';
import {IERC165} from '@openzeppelin/contracts/utils/introspection/IERC165.sol';

abstract contract OpenReactionBase is OspContext, IOpenReaction, IERC165 {
    constructor(address osp) OspContext(osp) {}

    function processReaction(
        uint256 profileId,
        uint256 referencedProfileId,
        uint256 referencedContentId,
        bytes calldata data
    ) external payable override onlyOsp {
        _processReaction(profileId, referencedProfileId, referencedContentId, data);
    }

    function _processReaction(
        uint256 profileId,
        uint256 referencedProfileId,
        uint256 referencedContentId,
        bytes calldata data
    ) internal virtual;

    function supportsInterface(bytes4 interfaceId) external pure override returns (bool) {
        return
            interfaceId == type(IOpenReaction).interfaceId ||
            interfaceId == type(IERC165).interfaceId;
    }
}
合同源代码
文件 94 的 124:OspClient.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

import './IGovernanceLogic.sol';
import './IProfileLogic.sol';
import './IContentLogic.sol';
import './IRelationLogic.sol';
import './ICommunityLogic.sol';
import '../../../libraries/OspErrors.sol';
import '../../../libraries/OspEvents.sol';

/**
 * @title OspClient
 * @author OpenSocial Protocol
 * @dev This is the interface for the OspClient contract,
 * @dev This contract is used to generate the OpenSocial abi file.
 */
interface OspClient is
    IGovernanceLogic,
    IProfileLogic,
    IContentLogic,
    IRelationLogic,
    ICommunityLogic
{

}
合同源代码
文件 95 的 124:OspContext.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

import '@openzeppelin/contracts/utils/Context.sol';
import '../logics/interfaces/OspClient.sol';

abstract contract OspContext is Context {
    OspClient immutable OSP;

    constructor(address osp) {
        if (osp == address(0)) revert OspErrors.InitParamsInvalid();
        OSP = OspClient(osp);
    }

    modifier onlyOsp() {
        if (_msgSender() != address(OSP)) revert OspErrors.NotOSP();
        _;
    }

    modifier onlyGov() {
        if (!OSP.hasRole(Constants.GOVERNANCE, _msgSender())) revert OspErrors.NotGovernance();
        _;
    }

    modifier onlyOperation() {
        if (!OSP.hasRole(Constants.OPERATION, _msgSender())) revert OspErrors.NotOperation();
        _;
    }

    modifier onlyProfile() {
        if (OSP.getProfileIdByAddress(_msgSender()) == 0) revert OspErrors.NotHasProfile();
        _;
    }

    modifier nonPayable() {
        if (msg.value != 0) revert OspErrors.InvalidValue();
        _;
    }
}
合同源代码
文件 96 的 124:OspDataTypes.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

/**
 * @title OspDataTypes
 * @author OpenSocial Protocol
 *
 * @dev The OspDataTypes library contains data types used throughout the OpenSocial Protocol.
 */
library OspDataTypes {
    bytes32 internal constant EIP712_DOMAIN_TYPEHASH =
        keccak256(
            'EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'
        );
    bytes32 internal constant CREATE_ACTIVITY_WITH_SIG_TYPEHASH =
        keccak256(
            'CreateActivityWithSig(uint256 profileId,uint256 communityId,string contentURI,bytes extensionInitCode,bytes referenceConditionInitCode,bytes ctx,uint256 nonce,uint256 deadline)'
        );

    bytes32 internal constant CREATE_OPEN_REACTION_WITH_SIG_TYPEHASH =
        keccak256(
            'CreateOpenReactionWithSig(uint256 profileId,uint256 communityId,uint256 referencedProfileId,uint256 referencedContentId,bytes reactionAndData,bytes referenceConditionData,bytes ctx,uint256 nonce,uint256 deadline)'
        );

    bytes32 internal constant CREATE_COMMENT_WITH_SIG_TYPEHASH =
        keccak256(
            'CreateCommentWithSig(uint256 profileId,uint256 communityId,string contentURI,uint256 referencedProfileId,uint256 referencedContentId,bytes referenceConditionInitCode,bytes referenceConditionData,bytes ctx,uint256 nonce,uint256 deadline)'
        );
    /*///////////////////////////////////////////////////////////////
                        Common Type
    //////////////////////////////////////////////////////////////*/

    /**
     * @dev An enum containing the different states the protocol can be in, limiting certain actions.
     *
     * @param Unpaused The fully unpaused state.
     * @param PublishingPaused The state where only content creation functions are paused.
     * @param Paused The fully paused state.
     */
    enum ProtocolState {
        Unpaused,
        PublishingPaused,
        Paused
    }

    /**
     * @dev A struct containing the necessary information to reconstruct an EIP-712 typed data signature.
     *
     * @param signature Signature
     * @param deadline The signature's deadline
     */
    struct EIP712Signature {
        address signer;
        bytes signature;
        uint256 deadline;
    }

    /*///////////////////////////////////////////////////////////////
                        Storage Struct
    //////////////////////////////////////////////////////////////*/

    /**
     * @dev A struct containing profile data.
     *
     * @param contentCount The number of publications made to this profile.
     * @param followCondition The address of the current follow condition in use by this profile, can be empty.
     * @param followSBT The address of the FollowSBT associated with this profile, can be empty..
     * @param handle The profile's associated handle.
     * @param owner The profile's owner.
     * @param dispatcher The profile's dispatcher.
     * @param mintTimestamp The timestamp at which this profile was minted.
     */
    struct ProfileStruct {
        uint256 contentCount;
        address followCondition;
        address followSBT;
        string handle;
        address owner;
        uint96 mintTimestamp;
        uint256 inviter;
    }

    /**
     * @dev A struct containing community data.
     * @param handle The community's associated handle.
     * @param joinCondition The address of the current join condition in use by this community, can be empty.
     * @param joinNFT The address of the JoinNFT associated with this community.
     * @param activityCondition The address of the current activity condition in use by this community, can be empty.
     */
    struct CommunityStruct {
        string handle;
        address joinCondition;
        address joinNFT;
        address activityCondition;
        address viewPrivacyCondition;
    }

    struct ContentStruct {
        uint256 communityId;
        uint256 referencedProfileId;
        uint256 referencedContentId;
        string contentURI;
        address extension;
        address referenceCondition;
    }

    struct PluginStruct {
        bool isEnable;
        address tokenAddress;
        uint256 amount;
    }

    /*///////////////////////////////////////////////////////////////
                        Call Params
    //////////////////////////////////////////////////////////////*/

    /**
     * @dev A struct containing the parameters required for the `createProfile()` function.
     *
     * @param handle The handle to set for the profile, must be unique and non-empty.
     * @param followCondition The follow condition to use, can be the zero address.
     * @param followConditionInitData The follow condition initialization data, if any.
     */
    struct CreateProfileData {
        string handle;
        bytes followConditionInitCode;
        uint256 inviter;
        bytes ctx;
    }

    /**
     * Create Activity Data
     * @param profileId The profile id of the creator.
     * @param communityId The community id of the content, if zero then it's a global content.
     * @param contentURI The URI of the content.
     * @param extensionInitCode If set, the extension will be set.
     * @param referenceConditionInitCode If set,the reference condition will be set. contains 20 bytes of condition address, followed by calldata.
     * @param ctx The context data.
     */
    struct CreateActivityData {
        uint256 profileId;
        uint256 communityId;
        string contentURI;
        bytes extensionInitCode;
        bytes referenceConditionInitCode;
        bytes ctx;
    }

    /**
     * Create Comment Data
     * @param profileId The profile id of the commenter.
     * @param communityId
     * @param contentURI
     * @param referencedProfileId
     * @param referencedContentId
     * @param referenceConditionInitCode If set,the reference condition will be set. contains 20 bytes of condition address, followed by calldata.
     * @param referenceConditionData The data passed to the reference condition.
     * @param ctx The context data.
     */
    struct CreateCommentData {
        uint256 profileId;
        uint256 communityId;
        string contentURI;
        uint256 referencedProfileId;
        uint256 referencedContentId;
        bytes referenceConditionInitCode;
        bytes referenceConditionData;
        bytes ctx;
    }

    /**
     * @dev A struct containing the parameters required for the `createMegaphone()` function.
     *
     * @param profileId The profile ID of the user creating the megaphone.
     * @param referencedProfileId The profile ID of the user who created the content being megaphoned.
     * @param referencedContentId The content ID being megaphoned.
     * @param tags The tags to associate with the megaphone.
     * @param startTime The start time of the megaphone.
     * @param duration The duration of the megaphone.
     * @param currency The currency to use for the megaphone.
     * @param amount The amount to pay for the megaphone.
     * @param ctx The context data.
     */
    struct CreateMegaphoneData {
        uint256 profileId;
        uint256 referencedProfileId;
        uint256 referencedContentId;
        string[] tags;
        uint256 startTime;
        uint256 duration;
        address currency;
        uint256 amount;
        bytes ctx;
    }

    /**
     * @dev A struct containing the parameters required for the `createOpenReaction()` function.
     *
     * @param profileId The profile ID of the user creating the reaction.
     * @param communityId The community ID of the content being reacted to.
     * @param referencedProfileId The profile ID of the user who created the content being reacted to.
     * @param referencedContentId The content ID being reacted to.
     * @param reactionAndData The reaction and data to use.
     * @param referenceConditionData The reference condition data to use.
     * @param ctx The context data.
     */
    struct CreateOpenReactionData {
        uint256 profileId;
        uint256 communityId;
        uint256 referencedProfileId;
        uint256 referencedContentId;
        uint256 reactionValue;
        bytes reactionAndData;
        bytes referenceConditionData;
        bytes ctx;
    }

    /**
     * @dev A struct containing the parameters required for the `createCommunity()` function.
     *
     * @param handle The handle to set for the community, must be unique and non-empty.
     * @param communityConditionAndData The community condition and data to use, can be the zero address.
     * @param joinConditionInitCode The join condition initialization data, if any.
     * @param tags The tags to associate with the community.
     * @param ctx The context data.
     */
    struct CreateCommunityData {
        string handle;
        bytes communityConditionAndData;
        bytes joinConditionInitCode;
        string[] tags;
        bytes ctx;
    }

    /**
     * @dev A struct containing the parameters required for the `follow()` function.
     *
     * @param profileId The profile token ID to follow.
     * @param data The data passed to the follow condition.
     * @param ctx The context data.
     */
    struct FollowData {
        uint256 profileId;
        bytes data;
        bytes ctx;
    }

    /**
     * @dev A struct containing the parameters required for the `batchFollow()` function.
     *
     * @param profileIds The array of profile token IDs to follow.
     * @param datas The array of follow condition data parameters to pass to each profile's follow condition.
     * @param ctx The context data.
     */
    struct BatchFollowData {
        uint256[] profileIds;
        bytes[] datas;
        uint256[] values;
        bytes ctx;
    }

    /**
     * @dev A struct containing the parameters required for the `join()` function.
     *
     * @param communityId The ID of the community to join.
     * @param data The data passed to the join condition.
     * @param ctx The context data.
     */
    struct JoinData {
        uint256 communityId;
        bytes data;
        bytes ctx;
    }

    /**
     * @dev A struct containing the parameters required for the `batchJoin()` function.
     * @param communityIds The array of community token IDs to join.
     * @param datas The array of join condition data parameters to pass to each community's join condition.
     * @param values The array of values to pass to each community's join condition.
     * @param ctx The context data.
     */
    struct BatchJoinData {
        uint256[] communityIds;
        bytes[] datas;
        uint256[] values;
        bytes ctx;
    }

    struct RoyaltyInfo {
        uint128 royaltyFraction;
        uint128 ospTreasureFraction;
    }
}
合同源代码
文件 97 的 124:OspErrors.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

library OspErrors {
    //Common Errors
    error ArrayMismatch();

    //EIP712 Errors
    error SignatureExpired();
    error SignatureInvalid();

    //ERC721 Errors
    error NotOwnerOrApproved();
    error TokenDoesNotExist();

    //SBT Errors
    error SBTTokenAlreadyExists();
    error SBTTransferNotAllowed();

    //JOIN NFT Errors
    error JoinNFTDuplicated();
    error JoinNFTUnauthorizedAccount();
    error JoinNFTBlocked();

    //OSP Errors
    error NotOSP();
    error InvalidSender();
    error NotGovernance();
    error NotOperation();
    error EmergencyAdminCannotUnpause();
    error AppNotWhitelisted();
    error ExtensionNotCommunityWhitelisted();
    error NotProfileOwner();
    error NotHasProfile();
    error ProfileDoesNotExist();
    error ContentDoesNotExist();
    error HandleTaken();
    error HandleLengthInvalid();
    error HandleContainsInvalidCharacters();
    error NotCommunityOwner();
    error NotJoinCommunity();
    error NotFollowProfile();
    error NotJoinNFT();
    error NotFollowSBT();
    error NotCommunityNFT();
    error ContentNotPublic();
    error TagDoesNotExist();
    error TooManyTags();
    error InitParamsInvalid();
    error ERC6551AccountImplNotDeployed();
    error DataMismatch();
    error InvalidToken();
    error InvalidContentURI();
    error InvalidProfileId();
    error InvalidCommunityId();
    error InvalidValue();
    error InvalidTreasure();
    error InvalidAddress();
    error InvalidParam();

    //Plugin Errors
    error PluginNotEnable();
    error PluginAlreadyPurchased();

    // MultiState Errors
    error Paused();
    error PublishingPaused();

    //Reaction Errors
    error ReactionInvalid();
}
合同源代码
文件 98 的 124:OspEvents.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

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

library OspEvents {
    event OSPInitialized(
        string name,
        string symbol,
        address followSBTImpl,
        address joinNFTImpl,
        address communityNFT,
        address communityAccountProxy,
        uint256 timestamp
    );

    /**
     * @dev Emitted when the osp state is set.
     *
     * @param caller The caller who set the state.
     * @param prevState The previous protocol state, an enum of either `Paused`, `PublishingPaused` or `Unpaused`.
     * @param newState The newly set state, an enum of either `Paused`, `PublishingPaused` or `Unpaused`.
     * @param timestamp The current block timestamp.
     */
    event StateSet(
        address indexed caller,
        OspDataTypes.ProtocolState indexed prevState,
        OspDataTypes.ProtocolState indexed newState,
        uint256 timestamp
    );

    event BaseURISet(string communityNFTBaseURI, uint256 timestamp);

    /**
     * @dev Emitted when a app is added to or removed from the whitelist.
     *
     * @param app The address of the app.
     * @param whitelisted Whether or not the reaction is being added to the whitelist.
     * @param timestamp The current block timestamp.
     */
    event AppWhitelisted(address indexed app, bool indexed whitelisted, uint256 timestamp);

    event TokenWhitelisted(address indexed token, bool indexed whitelisted, uint256 timestamp);

    /**
     * @dev Emitted when add or remove a reserve community handle.
     *
     * @param handleHash The hash of the handle to add or remove.
     * @param handle The handle to add or remove.
     * @param isReserved Whether or not the handle is being added to the reserve list.
     * @param timestamp The current block timestamp.
     */
    event CommunityHandleReserve(
        bytes32 indexed handleHash,
        bool indexed isReserved,
        string handle,
        uint256 timestamp
    );

    /**
     * @dev Emitted when a profile is created.
     *
     * @param profileId The newly created profile's token ID.
     * @param to The address receiving the profile with the given profile ID.
     * @param handle The handle set for the profile.
     * @param followCondition The profile's newly set follow condition. This CAN be the zero address.
     * @param timestamp The current block timestamp.
     */
    event ProfileCreated(
        uint256 indexed profileId,
        address indexed to,
        string handle,
        address followCondition,
        uint256 inviter,
        bytes ctx,
        uint256 timestamp
    );

    /**
     * @dev Emitted when a profile's follow condition is set.
     *
     * @param profileId The profile's token ID.
     * @param followCondition The profile's newly set follow condition. This CAN be the zero address.
     * @param timestamp The current block timestamp.
     */
    event FollowConditionSet(uint256 indexed profileId, address followCondition, uint256 timestamp);

    /**
     * @dev Emitted when a FollowSBT clone is deployed using a lazy deployment pattern.
     *
     * @param profileId The token ID of the profile to which this FollowSBT is associated.
     * @param followSBT The address of the newly deployed FollowSBT clone.
     * @param timestamp The current block timestamp.
     */
    event FollowSBTDeployed(
        uint256 indexed profileId,
        address indexed followSBT,
        uint256 timestamp
    );

    /**
     * @dev Emitted when a profile is updated.
     *
     * @param follower The address updating the profile.
     * @param followerProfileId The token ID of the profile updating the profile.
     * @param profileId The token ID of the profile being updated.
     * @param followConditionData The data passed to the follow condition.
     * @param tokenId The TokenNFT ID.
     * @param timestamp The current block timestamp.
     */
    event Followed(
        address indexed follower,
        uint256 indexed followerProfileId,
        uint256 profileId,
        bytes followConditionData,
        uint256 tokenId,
        bytes ctx,
        uint256 timestamp
    );

    /**
     * @dev Emitted upon a successful follow action.
     */
    event BatchFollowed(
        address indexed follower,
        uint256 followerProfileId,
        uint256[] profileIds,
        bytes[] followConditionDatas,
        uint256[] tokenIds,
        bytes ctx,
        uint256 timestamp
    );

    /**
     * @dev Emitted via callback when a FollowSBT is transferred.
     *
     * @param profileId The token ID of the profile associated with the FollowSBT being transferred.
     * @param followSBTId The FollowSBT being transferred's token ID.
     * @param from The address the FollowSBT is being transferred from.
     * @param to The address the FollowSBT is being transferred to.
     * @param timestamp The current block timestamp.
     */
    event FollowSBTTransferred(
        uint256 indexed profileId,
        uint256 indexed followSBTId,
        address from,
        address to,
        uint256 timestamp
    );

    /**
     * @dev Emitted via callback when a communityNFT is transferred.
     *
     * @param communityId The token ID of the community associated with the communityNFT being transferred.
     * @param from The address the communityNFT is being transferred from.
     * @param to The address the communityNFT is being transferred to.
     * @param timestamp The current block timestamp.
     */
    event CommunityNFTTransferred(
        uint256 indexed communityId,
        address from,
        address to,
        uint256 timestamp
    );

    /**
     * @dev Emitted via callback when a JoinNFT is transferred.
     *
     * @param joinNFTId The token ID of the profile associated with the JoinNFT being transferred.
     * @param from The address the JoinNFT is being transferred from.
     * @param to The address the JoinNFT is being transferred to.
     * @param timestamp The current block timestamp.
     */
    event JoinNFTTransferred(
        uint256 indexed communityId,
        uint256 indexed joinNFTId,
        address from,
        address to,
        uint256 timestamp
    );

    /**
     * @dev Emitted when a newly deployed follow NFT is initialized.
     *
     * @param profileId The token ID of the profile connected to this follow NFT.
     * @param timestamp The current block timestamp.
     */
    event FollowSBTInitialized(uint256 indexed profileId, uint256 timestamp);

    /**
     * @dev Emitted when a newly deployed join NFT is initialized.
     *
     * @param communityId The unique ID of the community mapped to this collect NFT.
     * @param timestamp The current block timestamp.
     */
    event JoinNFTInitialized(uint256 indexed communityId, uint256 timestamp);

    /**
     * @dev Emitted when a JoinNFT clone is deployed using a lazy deployment pattern.
     *
     * @param communityId The unique ID of the community mapped to this join NFT.
     * @param joinNFT The address of the newly deployed joinNFT clone.
     * @param timestamp The current block timestamp.
     */
    event JoinNFTDeployed(uint256 indexed communityId, address indexed joinNFT, uint256 timestamp);

    /**
     * @dev Emitted when a community is created.
     *
     * @param communityId The token ID of the community being created.
     * @param to The address receiving the community with the given community ID.
     * @param handle The handle set for the community.
     * @param communityConditionAndData Conditions for creating the community.
     * @param joinCondition The community's newly set join condition. This CAN be the zero address.
     * @param joinNFT The community's newly set join NFT.
     */
    event CommunityCreated(
        uint256 indexed communityId,
        address indexed to,
        string handle,
        bytes communityConditionAndData,
        address joinCondition,
        address joinNFT,
        string[] tags,
        bytes ctx,
        uint256 timestamp
    );

    /**
     * @dev Emitted when communityTag is updated.
     *
     * @param communityId The token ID of the community being updated.
     * @param tags The new tags for the community.
     */

    event CommunityTagsUpdated(
        uint256 indexed communityId,
        address indexed sender,
        string[] tags,
        uint256 timestamp
    );

    /**
     * @dev Emitted when you join a community.
     *
     * @param joiner The address joining the community.
     * @param joinerProfileId The token ID of the profile joining the community.
     * @param communityId The token ID of the community being joined.
     * @param joinConditionData The data passed to the join condition.
     * @param ctx The context passed to the join condition.
     */
    event Joined(
        address indexed joiner,
        uint256 joinerProfileId,
        uint256 communityId,
        bytes joinConditionData,
        uint256 tokenId,
        bytes ctx,
        uint256 timestamp
    );

    /**
     * @dev Emitted when a batch of communities are joined.
     */
    event BatchJoined(
        address indexed joiner,
        uint256 joinerProfileId,
        uint256[] communityIds,
        bytes[] joinConditionDatas,
        uint256[] tokenIds,
        bytes ctx,
        uint256 timestamp
    );

    /**
     * @dev Emitted when a community's join condition is set.
     *
     * @param communityId The community's token ID.
     * @param joinCondition The community's newly set join condition. This CAN be the zero address.
     * @param timestamp The current block timestamp.
     */
    event JoinConditionSet(
        uint256 indexed communityId,
        address indexed sender,
        address joinCondition,
        uint256 timestamp
    );

    /**
     * @dev Emitted when a community's activity condition is set.
     *
     * @param communityId The community's token ID.
     * @param activityCondition The community's newly set activity condition. This can be the zero address.
     * @param timestamp The current block timestamp.
     */
    event ActivityConditionSet(
        uint256 indexed communityId,
        address indexed sender,
        address activityCondition,
        uint256 timestamp
    );

    /**
     * @dev Emitted when a community's view privacy condition is set.
     *
     * @param communityId The community's token ID.
     * @param viewPrivacyCondition The community's newly set view privacy condition. This can be the zero address.
     * @param timestamp The current block timestamp.
     */
    event ViewPrivacyConditionSet(
        uint256 indexed communityId,
        address indexed sender,
        address viewPrivacyCondition,
        uint256 timestamp
    );

    event ActivityCreated(
        uint256 indexed profileId,
        uint256 indexed contentId,
        uint256 communityId,
        string contentURI,
        address extension,
        address referenceCondition,
        bytes ctx,
        uint256 timestamp
    );

    event CommentCreated(
        uint256 indexed profileId,
        uint256 indexed contentId,
        uint256 communityId,
        string contentURI,
        uint256 referencedProfileId,
        uint256 referencedContentId,
        address referenceCondition,
        bytes ctx,
        uint256 timestamp
    );

    event OpenReactionCreated(
        uint256 indexed profileId,
        uint256 indexed referencedProfileId,
        uint256 indexed referencedContentId,
        uint256 communityId,
        address openReaction,
        bytes openReactionData,
        bytes ctx,
        uint256 timestamp
    );

    event MegaphoneCreated(
        uint256 indexed megaphoneId,
        uint256 indexed referencedProfileId,
        uint256 indexed referencedContentId,
        uint256 profileId,
        string[] tags,
        uint256 startTime,
        uint256 duration,
        address currency,
        uint256 amount,
        bytes ctx,
        uint256 timestamp
    );

    event ERC6551AccountImplSet(address accountImpl, uint256 timestamp);

    event JoinNFTImplSet(address accountImpl, uint256 timestamp);

    event JoinNFTRoleChanged(
        uint256 indexed communityId,
        address indexed sender,
        address indexed account,
        uint256 role,
        uint256 timestamp
    );

    event JoinNFTAccountBlocked(
        uint256 indexed communityId,
        address indexed sender,
        address indexed account,
        bool isBlock,
        uint256 timestamp
    );

    event JoinNFTAccountLevelChanged(
        uint256 indexed communityId,
        uint256 indexed tokenId,
        address indexed sender,
        address account,
        address communityJoinNFT,
        uint256 level,
        uint256 timestamp
    );

    event CommunityExtensionWhitelistSet(
        uint256 indexed communityId,
        address indexed extension,
        bool indexed isWhitelist,
        address sender,
        uint256 timestamp
    );
}
合同源代码
文件 99 的 124:OspHelpers.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

import './OspDataTypes.sol';
import './OspErrors.sol';

/**
 * @title OspHelpers
 * @author OpenSocial Protocol
 *
 * @dev This is a library that only contains a single function that is used in the osp contract as well as in
 * both the publishing logic and interaction logic libraries.
 */
library OspHelpers {
    /**
     * @dev This helper function just returns the pointed content if the passed content is a mirror,
     * otherwise it returns the passed publication.
     *
     * @param profileId The token ID of the profile that published the given publication.
     * @param contentId The content ID of the given publication.
     * @param _contentByIdByProfile A pointer to the storage mapping of publications by contentId by profile ID.
     *
     * @return tuple First, the pointed publication's publishing profile ID, second, the pointed publication's ID, and third, the
     * pointed publication's collect . If the passed content is not a mirror, this returns the given publication.
     */
    function getPointedIfWithContentRoot(
        uint256 profileId,
        uint256 contentId,
        mapping(uint256 => mapping(uint256 => OspDataTypes.ContentStruct))
            storage _contentByIdByProfile
    ) internal view returns (uint256, uint256) {
        string memory contentURI = _contentByIdByProfile[profileId][contentId].contentURI;

        if (bytes(contentURI).length == 0) {
            uint256 pointedTokenId = _contentByIdByProfile[profileId][contentId]
                .referencedProfileId;

            if (pointedTokenId == 0) revert OspErrors.ContentDoesNotExist();

            uint256 pointedcontentId = _contentByIdByProfile[profileId][contentId]
                .referencedContentId;

            return (pointedTokenId, pointedcontentId);
        } else {
            return (profileId, contentId);
        }
    }
}
合同源代码
文件 100 的 124:OspLogicBase.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

import {OspStorage} from 'contracts/core/storage/OspStorage.sol';
import {OspMultiState} from 'contracts/core/base/OspMultiState.sol';
import {EIP712Base} from 'contracts/core/base/EIP712Base.sol';
import {OspErrors} from 'contracts/libraries/OspErrors.sol';
import {OspEvents} from 'contracts/libraries/OspEvents.sol';
import {OspDataTypes} from 'contracts/libraries/OspDataTypes.sol';
import {IJoinCondition} from 'contracts/interfaces/IJoinCondition.sol';
import {IOpenReaction} from 'contracts/interfaces/IOpenReaction.sol';
import {IActivityExtension} from 'contracts/interfaces/IActivityExtension.sol';
import {IReferenceCondition} from 'contracts/interfaces/IReferenceCondition.sol';
import {ICommunityCondition} from 'contracts/interfaces/ICommunityCondition.sol';
import {IFollowCondition} from 'contracts/interfaces/IFollowCondition.sol';
import {IActivityCondition} from 'contracts/interfaces/IActivityCondition.sol';
import {IViewPrivacyCondition} from 'contracts/interfaces/IViewPrivacyCondition.sol';

import {ERC165Checker} from '@openzeppelin/contracts/utils/introspection/ERC165Checker.sol';
import {AccessControlUpgradeable} from '@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol';

/**
 * @title OspLogicBase
 * @author OpenSocial Protocol
 * @dev The OspLogicBase contract includes methods for modifying contract state and some common methods that all Logic contracts must inherit.
 */
contract OspLogicBase is OspMultiState, OspStorage, EIP712Base {
    /*///////////////////////////////////////////////////////////////
                        modifiers
    //////////////////////////////////////////////////////////////*/

    /*///////////////////////////////////////////////////////////////
                        Internal functions
    //////////////////////////////////////////////////////////////*/

    function _checkFollowCondition(address followCondition) internal view {
        if (
            !_getGovernanceStorage()._appWhitelisted[followCondition] ||
            !ERC165Checker.supportsInterface(followCondition, type(IFollowCondition).interfaceId)
        ) revert OspErrors.AppNotWhitelisted();
    }

    function _checkCommunityCondition(address communityCondition) internal view {
        if (
            !_getGovernanceStorage()._appWhitelisted[communityCondition] ||
            !ERC165Checker.supportsInterface(
                communityCondition,
                type(ICommunityCondition).interfaceId
            )
        ) revert OspErrors.AppNotWhitelisted();
    }

    function _checkJoinCondition(address joinCondition) internal view {
        if (
            !_getGovernanceStorage()._appWhitelisted[joinCondition] ||
            !ERC165Checker.supportsInterface(joinCondition, type(IJoinCondition).interfaceId)
        ) revert OspErrors.AppNotWhitelisted();
    }

    function _checkActivityCondition(address activityCondition) internal view {
        if (
            !_getGovernanceStorage()._appWhitelisted[activityCondition] ||
            !ERC165Checker.supportsInterface(
                activityCondition,
                type(IActivityCondition).interfaceId
            )
        ) revert OspErrors.AppNotWhitelisted();
    }

    function _checkViewPrivacyCondition(address privacyCondition) internal view {
        if (
            !_getGovernanceStorage()._appWhitelisted[privacyCondition] ||
            !ERC165Checker.supportsInterface(
                privacyCondition,
                type(IViewPrivacyCondition).interfaceId
            )
        ) revert OspErrors.AppNotWhitelisted();
    }

    function _checkActivityExtension(address extension) internal view {
        if (
            !_getGovernanceStorage()._appWhitelisted[extension] ||
            !ERC165Checker.supportsInterface(extension, type(IActivityExtension).interfaceId)
        ) revert OspErrors.AppNotWhitelisted();
    }

    function _checkOpenReaction(address openReaction) internal view {
        if (
            !_getGovernanceStorage()._appWhitelisted[openReaction] ||
            !ERC165Checker.supportsInterface(openReaction, type(IOpenReaction).interfaceId)
        ) revert OspErrors.AppNotWhitelisted();
    }

    function _checkReferenceCondition(address referenceCondition) internal view {
        if (
            !_getGovernanceStorage()._appWhitelisted[referenceCondition] ||
            !ERC165Checker.supportsInterface(
                referenceCondition,
                type(IReferenceCondition).interfaceId
            )
        ) revert OspErrors.AppNotWhitelisted();
    }

    function _hashRole(bytes32 role, address account) internal view returns (bool) {
        AccessControlUpgradeable.AccessControlStorage storage $;
        assembly {
            $.slot := 0x02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800
        }
        return $._roles[role].hasRole[account];
    }

    /**
     *  @dev This function reverts if the caller is not the owner of the profile.
     */
    function _validateIsProfileOwner(address addr, uint256 profileId) internal view {
        if (addr != _ownerOf(profileId)) revert OspErrors.NotProfileOwner();
    }

    /**
     *  @dev This function reverts if the address is not has profile.
     */
    function _validateHasProfile(address addr) internal view returns (uint256) {
        uint256 profileId = _getProfileStorage()._profileIdByAddress[addr];
        if (profileId == 0) revert OspErrors.NotHasProfile();
        return profileId;
    }

    /**
     * @dev Returns the owner of the profile.
     */
    function _ownerOf(uint256 profileId) internal view returns (address) {
        return _getProfileStorage()._profileById[profileId].owner;
    }

    function _calculateDomainSeparator() internal view virtual override returns (bytes32) {
        return
            keccak256(
                abi.encode(
                    OspDataTypes.EIP712_DOMAIN_TYPEHASH,
                    keccak256(bytes(_getGovernanceStorage()._name)),
                    EIP712_REVISION_HASH,
                    block.chainid,
                    address(this)
                )
            );
    }
}
合同源代码
文件 101 的 124:OspMultiState.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

import '../../libraries/OspEvents.sol';
import '../../libraries/OspDataTypes.sol';
import '../../libraries/OspErrors.sol';

/**
 * @title OspMultiState
 *
 * @dev This is an abstract contract that implements internal state setting and validation.
 *
 * whenNotPaused: Either publishingPaused or Unpaused.
 * whenPublishingEnabled: When Unpaused only.
 */
abstract contract OspMultiState {
    struct ProtocolStateStorage {
        OspDataTypes.ProtocolState state;
    }
    bytes32 internal constant STATE_STORAGE_POSITION = keccak256('osp.state.storage');

    modifier whenNotPaused() {
        _validateNotPaused();
        _;
    }

    modifier whenPublishingEnabled() {
        _validatePublishingEnabled();
        _;
    }

    function protocolStateStorage()
        internal
        pure
        returns (ProtocolStateStorage storage protocolState)
    {
        bytes32 position = STATE_STORAGE_POSITION;
        assembly {
            protocolState.slot := position
        }
    }

    /**
     * @dev Returns the current protocol state.
     *
     * @return ProtocolState The Protocol state, an enum, where:
     *      0: Unpaused
     *      1: PublishingPaused
     *      2: Paused
     */
    function _getState() internal view returns (OspDataTypes.ProtocolState) {
        return protocolStateStorage().state;
    }

    function _setState(OspDataTypes.ProtocolState newState) internal {
        OspDataTypes.ProtocolState prevState = protocolStateStorage().state;
        protocolStateStorage().state = newState;
        emit OspEvents.StateSet(msg.sender, prevState, newState, block.timestamp);
    }

    function _validatePublishingEnabled() internal view {
        if (protocolStateStorage().state != OspDataTypes.ProtocolState.Unpaused) {
            revert OspErrors.PublishingPaused();
        }
    }

    function _validateNotPaused() internal view {
        if (protocolStateStorage().state == OspDataTypes.ProtocolState.Paused)
            revert OspErrors.Paused();
    }
}
合同源代码
文件 102 的 124:OspNFTBase.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

import {ERC721BurnableUpgradeable} from '@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721BurnableUpgradeable.sol';
import {ERC721EnumerableUpgradeable} from '@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721EnumerableUpgradeable.sol';
import {ERC721Upgradeable} from '@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol';

/**
 * @title OspNFTBase
 * @author OpenSocial Protocol
 *
 * @dev This is an abstract base contract to be inherited by other OpenSocial Protocol NFTs, it includes
 * the ERC721EnumerableUpgradeable and ERC721BurnableUpgradeable.
 */
abstract contract OspNFTBase is ERC721EnumerableUpgradeable, ERC721BurnableUpgradeable {
    function _initialize(string memory name, string memory symbol) internal initializer {
        __ERC721_init(name, symbol);
    }

    function _update(
        address to,
        uint256 tokenId,
        address auth
    )
        internal
        override(ERC721EnumerableUpgradeable, ERC721Upgradeable)
        returns (address previousOwner)
    {
        previousOwner = ERC721EnumerableUpgradeable._update(to, tokenId, auth);
        _afterTokenTransfer(previousOwner, to, tokenId);
    }

    function _afterTokenTransfer(address from, address to, uint256 tokenId) internal virtual {}

    function supportsInterface(
        bytes4 interfaceId
    ) public view virtual override(ERC721EnumerableUpgradeable, ERC721Upgradeable) returns (bool) {
        return ERC721EnumerableUpgradeable.supportsInterface(interfaceId);
    }

    function _increaseBalance(
        address account,
        uint128 amount
    ) internal virtual override(ERC721EnumerableUpgradeable, ERC721Upgradeable) {
        ERC721EnumerableUpgradeable._increaseBalance(account, amount);
    }
}
合同源代码
文件 103 的 124:OspRouterImmutable.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

import './IRouter.sol';
import '@openzeppelin/contracts/utils/introspection/ERC165.sol';
import '@openzeppelin/contracts/utils/structs/EnumerableSet.sol';
import '@openzeppelin/contracts/proxy/Proxy.sol';
import '@openzeppelin/contracts/utils/Multicall.sol';

contract OspRouterImmutable is Multicall, ERC165, IRouter, Proxy {
    using EnumerableSet for EnumerableSet.Bytes32Set;
    /*///////////////////////////////////////////////////////////////
                    struct and event definitions
    //////////////////////////////////////////////////////////////*/
    struct Data {
        address admin;
        EnumerableSet.Bytes32Set allSelectors;
        mapping(address => EnumerableSet.Bytes32Set) selectorsForRouter;
        mapping(bytes4 => Router) routerForSelector;
        address pendingAdmin;
        uint96 pendingAdminTimestamp;
    }

    /**
     * @dev Emitted when the admin account has changed.
     */
    event AdminChanged(address previousAdmin, address newAdmin);

    event ChangeAdminStarted(address pendingAdmin, uint256 pendingAdminTimestamp);

    event PendingAdminRevoked(address pendingAdmin);

    /*///////////////////////////////////////////////////////////////
                    Constructor + initializer logic
    //////////////////////////////////////////////////////////////*/

    constructor(address admin_) {
        _changeAdmin(admin_);
    }

    /*///////////////////////////////////////////////////////////////
                            routerStorage
    //////////////////////////////////////////////////////////////*/

    bytes32 internal constant ROUTER_STORAGE_POSITION = keccak256('osp.router.storage');

    function routerStorage() internal pure returns (Data storage routerData) {
        bytes32 position = ROUTER_STORAGE_POSITION;
        assembly {
            routerData.slot := position
        }
    }

    /*///////////////////////////////////////////////////////////////
                                ERC 165
    //////////////////////////////////////////////////////////////*/

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        if (interfaceId == type(IRouter).interfaceId || super.supportsInterface(interfaceId)) {
            return true;
        } else {
            address implementation = _implementation();
            return
                implementation == address(0)
                    ? false
                    : IERC165(implementation).supportsInterface(interfaceId);
        }
    }

    /*///////////////////////////////////////////////////////////////
                        Generic contract logic
    //////////////////////////////////////////////////////////////*/
    modifier onlyAdmin() {
        Data storage data = routerStorage();
        require(msg.sender == data.admin, 'Router: Not authorized.');
        _;
    }

    /*///////////////////////////////////////////////////////////////
                        External functions
    //////////////////////////////////////////////////////////////*/

    function changeAdmin(address _newAdmin) public onlyAdmin {
        Data storage data = routerStorage();
        data.pendingAdmin = _newAdmin;
        data.pendingAdminTimestamp = uint96(block.timestamp);
        emit ChangeAdminStarted(_newAdmin, block.timestamp);
    }

    function revokePendingAdmin() public onlyAdmin {
        Data storage data = routerStorage();
        address pendingAdmin = data.pendingAdmin;
        data.pendingAdmin = address(0);
        data.pendingAdminTimestamp = 0;
        emit PendingAdminRevoked(pendingAdmin);
    }

    function acceptAdminRole() public {
        Data storage data = routerStorage();
        require(
            msg.sender == data.pendingAdmin &&
                block.timestamp > data.pendingAdminTimestamp + 1 days,
            'Router: Admin role not available.'
        );
        _changeAdmin(data.pendingAdmin);
        data.pendingAdmin = address(0);
        data.pendingAdminTimestamp = 0;
    }

    function getAdmin() public view returns (address) {
        Data storage data = routerStorage();
        return data.admin;
    }

    function getPendingAdmin() public view returns (address) {
        Data storage data = routerStorage();
        return data.pendingAdmin;
    }

    function getPendingAdminTimestamp() public view returns (uint96) {
        Data storage data = routerStorage();
        return data.pendingAdminTimestamp;
    }

    /// @dev Add functionality to the contract.
    function addRouter(Router memory _router) public onlyAdmin {
        _addRouter(_router);
    }

    /// @dev Update or override existing functionality.
    function updateRouter(Router memory _router) public onlyAdmin {
        _updateRouter(_router);
    }

    /// @dev Remove existing functionality from the contract.
    function removeRouter(bytes4 selector, string calldata functionSignature) public onlyAdmin {
        _removeRouter(selector, functionSignature);
    }

    /*///////////////////////////////////////////////////////////////
                            View functions
    //////////////////////////////////////////////////////////////*/

    /// @dev View address of the plugged-in functionality contract for a given function signature.
    function getRouterForFunction(bytes4 _selector) public view returns (address) {
        return _getRouterForFunction(_selector);
    }

    /// @dev View all funtionality as list of function signatures.
    function getAllFunctionsOfRouter(
        address _routerAddress
    ) public view returns (bytes4[] memory registered) {
        Data storage data = routerStorage();
        uint256 count = data.selectorsForRouter[_routerAddress].length();

        registered = new bytes4[](count);
        for (uint256 i; i < count; i += 1) {
            registered[i] = bytes4(data.selectorsForRouter[_routerAddress].at(i));
        }
    }

    /// @dev View all funtionality existing on the contract.
    function getAllRouters() public view returns (Router[] memory registered) {
        Data storage data = routerStorage();
        EnumerableSet.Bytes32Set storage selectors = data.allSelectors;
        uint256 count = selectors.length();
        registered = new Router[](count);
        for (uint256 i; i < count; i += 1) {
            registered[i] = data.routerForSelector[bytes4(selectors.at(i))];
        }
    }

    /*///////////////////////////////////////////////////////////////
                        Internal functions
    //////////////////////////////////////////////////////////////*/
    function _implementation() internal view virtual override returns (address) {
        address router = _getRouterForFunction(msg.sig);
        require(router != address(0), 'Router: Not found.');
        return router;
    }

    /// @dev View address of the plugged-in functionality contract for a given function signature.
    function _getRouterForFunction(bytes4 _selector) public view returns (address) {
        Data storage data = routerStorage();
        return data.routerForSelector[_selector].routerAddress;
    }

    /// @dev Add functionality to the contract.
    function _addRouter(Router memory _router) internal {
        Data storage data = routerStorage();
        require(
            data.allSelectors.add(bytes32(_router.functionSelector)),
            'Router: router exists for function.'
        );
        require(
            _router.functionSelector ==
                bytes4(keccak256(abi.encodePacked(_router.functionSignature))),
            'Router: fn selector and signature mismatch.'
        );

        data.routerForSelector[_router.functionSelector] = _router;
        data.selectorsForRouter[_router.routerAddress].add(bytes32(_router.functionSelector));

        emit RouterAdded(_router.functionSelector, _router.routerAddress);
    }

    /// @dev Update or override existing functionality.
    function _updateRouter(Router memory _router) internal {
        address currentRouter = getRouterForFunction(_router.functionSelector);
        require(currentRouter != address(0), 'Router: No router available for selector.');
        require(
            _router.functionSelector ==
                bytes4(keccak256(abi.encodePacked(_router.functionSignature))),
            'Router: fn selector and signature mismatch.'
        );

        Data storage data = routerStorage();
        data.allSelectors.add(bytes32(_router.functionSelector));
        data.routerForSelector[_router.functionSelector] = _router;
        data.selectorsForRouter[currentRouter].remove(bytes32(_router.functionSelector));
        data.selectorsForRouter[_router.routerAddress].add(bytes32(_router.functionSelector));

        emit RouterUpdated(_router.functionSelector, currentRouter, _router.routerAddress);
    }

    /// @dev Remove existing functionality from the contract.
    function _removeRouter(bytes4 _selector, string calldata _functionSignature) internal {
        Data storage data = routerStorage();
        address currentRouter = _getRouterForFunction(_selector);
        require(currentRouter != address(0), 'Router: No router available for selector.');
        require(
            _selector == bytes4(keccak256(abi.encodePacked(_functionSignature))),
            'Router: fn selector and signature mismatch.'
        );

        delete data.routerForSelector[_selector];
        data.allSelectors.remove(_selector);
        data.selectorsForRouter[currentRouter].remove(bytes32(_selector));

        emit RouterRemoved(_selector, currentRouter);
    }

    function _changeAdmin(address admin_) internal {
        Data storage data = routerStorage();
        emit AdminChanged(data.admin, admin_);
        data.admin = admin_;
    }
}
合同源代码
文件 104 的 124:OspSBTBase.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

import {OspNFTBase} from './OspNFTBase.sol';
import {OspErrors} from '../../libraries/OspErrors.sol';

/**
 * @title OspSBTBase
 * @author OpenSocial Protocol
 *
 * @dev This is an abstract base contract to be inherited by other OpenSocial Protocol SBTs, it includes
 * the ERC721EnumerableUpgradeable and ERC721BurnableUpgradeable.
 * SBTs are not transferable, they can only be minted and burned.
 */
abstract contract OspSBTBase is OspNFTBase {
    function _afterTokenTransfer(
        address from,
        address to,
        uint256 /*tokenId*/
    ) internal virtual override {
        if (from != address(0) && to != address(0)) {
            revert OspErrors.SBTTransferNotAllowed();
        }
        if (from == address(0) && to != address(0) && balanceOf(to) > 1) {
            revert OspErrors.SBTTokenAlreadyExists();
        }
    }
}
合同源代码
文件 105 的 124:OspStorage.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

import '../../libraries/OspDataTypes.sol';
import {EnumerableSet} from '@openzeppelin/contracts/utils/structs/EnumerableSet.sol';

/**
 * @title OspStorage
 * @author OpenSocial Protocol
 *
 * @dev This abstract contract defines storage for the Osp protocol.
 * Each condition' storage in a different slot.
 * The order within the condition storage structure cannot be changed.
 */
abstract contract OspStorage {
    /*///////////////////////////////////////////////////////////////
                            ProfileStorage
    //////////////////////////////////////////////////////////////*/
    bytes32 internal constant PROFILE_STORAGE_POSITION = keccak256('osp.profile.storage');
    struct ProfileStorage {
        // Array with all token ids, used for enumeration
        uint256[] _allTokens;
        // Mapping from token id to position in the allTokens array
        mapping(uint256 => uint256) _allTokensIndex;
        mapping(bytes32 => uint256) _profileIdByHandleHash;
        mapping(uint256 => OspDataTypes.ProfileStruct) _profileById;
        mapping(address => uint256) _profileIdByAddress;
        uint256 _profileCounter;
        mapping(address => uint256) _sigNonces;
    }

    function _getProfileStorage() internal pure returns (ProfileStorage storage profileStorage) {
        bytes32 position = PROFILE_STORAGE_POSITION;
        assembly {
            profileStorage.slot := position
        }
    }

    /*///////////////////////////////////////////////////////////////
                            ContentStorage
    //////////////////////////////////////////////////////////////*/
    bytes32 internal constant PUBLICATION_STORAGE_POSITION = keccak256('osp.content.storage');
    struct ContentStorage {
        mapping(uint256 => mapping(uint256 => OspDataTypes.ContentStruct)) _contentByIdByProfile;
        //tag 最多10个
        uint256 _megaphoneCount;
    }

    function _getContentStorage()
        internal
        pure
        returns (ContentStorage storage publicationStorage)
    {
        bytes32 position = PUBLICATION_STORAGE_POSITION;
        assembly {
            publicationStorage.slot := position
        }
    }

    /*///////////////////////////////////////////////////////////////
                            GovernanceStorage
    //////////////////////////////////////////////////////////////*/
    bytes32 internal constant GOVERNANCE_STORAGE_POSITION = keccak256('osp.governance.storage');
    struct GovernanceStorage {
        string _name;
        string _symbol;
        address _followSBTImpl;
        address _joinNFTImpl;
        address _communityNFT;
        address _communityAccountProxy;
        address _erc6551AccountImpl;
        string _baseURI;
        mapping(address => bool) _appWhitelisted;
        mapping(bytes32 => bool) _reserveCommunityHandleHash;
        mapping(address => bool) _tokenWhitelisted;
        mapping(address => bool) DEPRECATED_superCommunityCreatorWhitelisted;
        address _treasure;
        OspDataTypes.RoyaltyInfo _joinNFTRoyaltyInfo;
    }

    function _getGovernanceStorage()
        internal
        pure
        returns (GovernanceStorage storage governanceStorage)
    {
        bytes32 position = GOVERNANCE_STORAGE_POSITION;
        assembly {
            governanceStorage.slot := position
        }
    }

    /*///////////////////////////////////////////////////////////////
                          CommunityStorage
    //////////////////////////////////////////////////////////////*/
    bytes32 internal constant COMMUNITY_STORAGE_POSITION = keccak256('osp.community.storage');
    struct CommunityStorage {
        mapping(uint256 => OspDataTypes.CommunityStruct) _communityById;
        mapping(bytes32 => uint256) _communityIdByHandleHash;
        mapping(uint256 => EnumerableSet.AddressSet) _activityExtensionWhitelistByCommunity;
    }

    function _getCommunityStorage()
        internal
        pure
        returns (CommunityStorage storage communityStorage)
    {
        bytes32 position = COMMUNITY_STORAGE_POSITION;
        assembly {
            communityStorage.slot := position
        }
    }
}
合同源代码
文件 106 的 124:OspUniversalProxy.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

import '../core/logics/interfaces/OspClient.sol';
import '../core/base/OspContext.sol';

import {ERC1967Utils} from '@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol';
import {ERC1967Proxy} from '@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol';

contract OspUniversalProxy is OspContext, ERC1967Proxy {
    constructor(
        address osp,
        address implementation,
        bytes memory data
    ) OspContext(osp) ERC1967Proxy(implementation, data) {}

    function updateToAndCall(address newImplementation, bytes memory data) external onlyGov {
        ERC1967Utils.upgradeToAndCall(newImplementation, data);
    }
}
合同源代码
文件 107 的 124:Payment.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

import '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';
import '@openzeppelin/contracts/token/ERC20/IERC20.sol';

library Payment {
    using SafeERC20 for IERC20;

    function payNative(address to, uint256 amount) internal {
        (bool success, ) = to.call{value: amount}('');
        require(success, 'Transfer failed');
    }

    function payERC20(address token, address from, address to, uint256 amount) internal {
        IERC20 payToken = IERC20(token);
        payToken.safeTransferFrom(from, to, amount);
    }

    function payERC20(address token, address to, uint256 amount) internal {
        IERC20 payToken = IERC20(token);
        payToken.safeTransfer(to, amount);
    }
}
合同源代码
文件 108 的 124:PresaleSigCommunityCond.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

import {CommunityCondBase} from '../../base/CommunityCondBase.sol';
import {Payment} from '../../../libraries/Payment.sol';
import {ECDSA} from '@openzeppelin/contracts/utils/cryptography/ECDSA.sol';
import {CondErrors} from '../libraries/CondErrors.sol';
import {CondDataTypes} from '../libraries/CondDataTypes.sol';
import {CondHelpers} from '../libraries/CondHelpers.sol';
import {MessageHashUtils} from '@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol';
import {IERC721} from '@openzeppelin/contracts/token/ERC721/IERC721.sol';

/**
 * @title PresaleSigCommunityCond
 * @author OpenSocial Protocol
 *
 * @dev This contract specifies that pay the specified amount of ETH to create the community on presale time.
 * The amount of ETH paid is related to the handle length of the community.
 * The community creation must be signed by the specified signer.
 * The signer must sign the ticket and tokenId(ticket is ERC721), target addr must hold the token.
 * If the official sale time is reached, the contract will automatically expire.
 */
contract PresaleSigCommunityCond is CommunityCondBase {
    event PresaleTimeSet(uint256 indexed presaleStartTime, uint256 timestamp);
    event SignerSet(address indexed signer, uint256 timestamp);

    event PresaleSigPaid(
        address indexed to,
        address indexed ticket,
        uint256 indexed tokenId,
        uint256 price,
        string handle,
        uint256 timestamp
    );

    using ECDSA for bytes32;
    using MessageHashUtils for bytes32;

    uint256 public presaleStartTime;
    address immutable fixedFeeCommunityCond;
    address signer;
    mapping(address => mapping(uint256 => bool)) _ticketUsed;

    constructor(
        address osp,
        address _fixedFeeCommunityCond,
        address _signer,
        uint256 _presaleStartTime
    ) CommunityCondBase(osp) {
        fixedFeeCommunityCond = _fixedFeeCommunityCond;
        signer = _signer;
        emit SignerSet(signer, block.timestamp);
        _setPresaleStartTime(_presaleStartTime);
        emit PresaleTimeSet(presaleStartTime, block.timestamp);
    }

    /**
     * @dev process create community, pay the specified amount of ETH to create the community on presale time.
     */
    function _processCreateCommunity(
        address to,
        string calldata handle,
        bytes calldata data
    ) internal override {
        CondDataTypes.FixedFeeCondData memory fixedFeeCondData = _getFixedFeeCondData();
        if (!isPresaleTime()) {
            revert CondErrors.NotPresaleTime();
        }
        (address ticket, uint256 tokenId) = _validateTicketAndSig(to, data);
        uint256 price = CondHelpers.getHandleETHPrice(handle, fixedFeeCondData);
        _charge(price, to);
        emit PresaleSigPaid(to, ticket, tokenId, price, handle, block.timestamp);
    }

    /**
     * @dev Set the presale start time, must be less than the official sale time
     */
    function setPresaleStartTime(uint256 _presaleStartTime) external onlyOperation {
        _setPresaleStartTime(_presaleStartTime);
        emit PresaleTimeSet(presaleStartTime, block.timestamp);
    }

    /**
     * @dev Set the signer address
     */
    function setSigner(address _signer) external onlyOperation {
        signer = _signer;
        emit SignerSet(signer, block.timestamp);
    }

    /**
     * @dev Get the handle price based on the length of the handle.
     */
    function getHandlePrice(string calldata handle) external view returns (uint256) {
        CondDataTypes.FixedFeeCondData memory fixedFeeCondData = _getFixedFeeCondData();
        return CondHelpers.getHandleETHPrice(handle, fixedFeeCondData);
    }

    function isTicketUsable(
        address ticket,
        uint256 tokenId,
        address holder
    ) external view returns (bool) {
        return !_ticketUsed[ticket][tokenId] && IERC721(ticket).ownerOf(tokenId) == holder;
    }

    function isPresaleTime() public view returns (bool) {
        return
            block.timestamp >= presaleStartTime &&
            block.timestamp < _getFixedFeeCondData().createStartTime;
    }

    function getPresaleTime() public view returns (uint256 start, uint256 end) {
        start = presaleStartTime;
        end = _getFixedFeeCondData().createStartTime;
    }

    /**
     * @dev Get the fixed fee condition data from fixFeeCommunityCond contract.
     */
    function _getFixedFeeCondData() internal view returns (CondDataTypes.FixedFeeCondData memory) {
        (bool success, bytes memory returnData) = fixedFeeCommunityCond.staticcall(
            abi.encodeWithSignature('fixedFeeCondData()')
        );
        require(success, 'call fixFeeCommunityCond failed');
        return abi.decode(returnData, (CondDataTypes.FixedFeeCondData));
    }

    function _setPresaleStartTime(uint256 _presaleStartTime) internal {
        CondDataTypes.FixedFeeCondData memory fixFeeCondData = _getFixedFeeCondData();
        require(_presaleStartTime < fixFeeCondData.createStartTime, 'Invalid time');
        presaleStartTime = _presaleStartTime;
    }

    function _charge(uint256 price, address to) internal virtual {
        if (msg.value < price) {
            revert CondErrors.InsufficientPayment();
        }
        uint256 overpayment;
        unchecked {
            overpayment = msg.value - price;
        }
        if (overpayment > 0) {
            Payment.payNative(to, overpayment);
        }
        address treasure = OSP.getTreasureAddress();
        require(treasure != address(0), 'Invalid treasure');
        Payment.payNative(treasure, price);
    }

    function _validateTicketAndSig(
        address to,
        bytes calldata data
    ) internal returns (address, uint256) {
        (
            address ticket,
            uint256 tokenId,
            address holder,
            address target,
            uint256 chainid,
            bytes memory signature
        ) = abi.decode(data, (address, uint256, address, address, uint256, bytes));
        // the signer determine the relationship between holder and target
        if (
            (_ticketUsed[ticket][tokenId] || IERC721(ticket).ownerOf(tokenId) != holder) ||
            target != to ||
            chainid != block.chainid
        ) {
            revert CondErrors.InvalidTicket();
        }
        bytes32 hash = keccak256(abi.encodePacked(ticket, tokenId, holder, target, chainid));
        if (hash.toEthSignedMessageHash().recover(signature) != signer) {
            revert CondErrors.SignatureInvalid();
        }
        _ticketUsed[ticket][tokenId] = true;
        return (ticket, tokenId);
    }
}
合同源代码
文件 109 的 124:ProfileLogic.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

import './interfaces/IProfileLogic.sol';
import './OspLogicBase.sol';
import '../../libraries/Constants.sol';
import '../../interfaces/IFollowCondition.sol';
import '@openzeppelin/contracts/utils/Strings.sol';

/**
 * @title ProfileLogic
 * @author OpenSocial Protocol
 * @dev This contract is the logic contract for interacting with profiles.
 */
contract ProfileLogic is IProfileLogic, OspLogicBase {
    using Strings for uint256;

    /*///////////////////////////////////////////////////////////////
                        Public functions
    //////////////////////////////////////////////////////////////*/

    /// @inheritdoc IProfileLogic
    function createProfile(
        OspDataTypes.CreateProfileData calldata vars
    ) external override whenNotPaused returns (uint256) {
        return _createProfile(vars);
    }

    /// @inheritdoc IProfileLogic
    function setFollowCondition(
        uint256 profileId,
        bytes calldata followConditionInitCode
    ) external override whenNotPaused {
        _validateIsProfileOwner(msg.sender, profileId);
        _setFollowCondition(profileId, followConditionInitCode);
    }

    /**
     * @dev opensocial not support.
     */
    function burn(uint256 /*profileId*/) external view override whenNotPaused {
        revert OspErrors.SBTTransferNotAllowed();
    }

    /**
     * @dev opensocial not support.
     */
    function safeTransferFrom(
        address /*from*/,
        address /*to*/,
        uint256 /*tokenId*/,
        bytes calldata /*data*/
    ) external pure override {
        revert OspErrors.SBTTransferNotAllowed();
    }

    /**
     * @dev opensocial not support.
     */
    function safeTransferFrom(
        address /*from*/,
        address /*to*/,
        uint256 /*tokenId*/
    ) external pure override {
        revert OspErrors.SBTTransferNotAllowed();
    }

    /**
     * @dev opensocial not support.
     */
    function transferFrom(
        address /*from*/,
        address /*to*/,
        uint256 /*tokenId*/
    ) external pure override {
        revert OspErrors.SBTTransferNotAllowed();
    }

    /**
     * @dev opensocial not support.
     */
    function approve(address /*to*/, uint256 /*tokenId*/) external pure override {
        revert OspErrors.SBTTransferNotAllowed();
    }

    /**
     * @dev opensocial not support.
     */
    function setApprovalForAll(address /*operator*/, bool /*_approved*/) external pure override {
        revert OspErrors.SBTTransferNotAllowed();
    }

    /*///////////////////////////////////////////////////////////////
                        Public Read functions
    //////////////////////////////////////////////////////////////*/
    /// @inheritdoc IERC165
    function supportsInterface(bytes4 interfaceId) external pure override returns (bool) {
        return
            interfaceId == type(IERC721Enumerable).interfaceId ||
            interfaceId == type(IERC721).interfaceId ||
            interfaceId == type(IERC721Metadata).interfaceId ||
            interfaceId == type(IERC165).interfaceId;
    }

    /// @inheritdoc IERC721
    function ownerOf(uint256 tokenId) public view override returns (address) {
        require(
            _getProfileStorage()._profileById[tokenId].owner != address(0),
            'ERC721: owner query for nonexistent token'
        );
        return _getProfileStorage()._profileById[tokenId].owner;
    }

    /// @inheritdoc IERC721Metadata
    function name() external view override returns (string memory) {
        return _getGovernanceStorage()._name;
    }

    /// @inheritdoc IERC721Metadata
    function symbol() external view override returns (string memory) {
        return _getGovernanceStorage()._symbol;
    }

    // @inheritdoc IERC721Metadata
    function tokenURI(uint256 tokenId) external view override returns (string memory) {
        string memory baseURI = _getGovernanceStorage()._baseURI;
        return
            bytes(baseURI).length > 0
                ? string(abi.encodePacked(baseURI, 'profile/', tokenId.toString()))
                : tokenId.toString();
    }

    /// @inheritdoc IERC721
    function balanceOf(address owner) public view override returns (uint256) {
        return _getProfileStorage()._profileIdByAddress[owner] != 0 ? 1 : 0;
    }

    /// @inheritdoc IERC721Enumerable
    function totalSupply() external view override returns (uint256) {
        return _getProfileStorage()._allTokens.length;
    }

    /// @inheritdoc IProfileLogic
    function nonces(address singer) external view override returns (uint256) {
        return _getProfileStorage()._sigNonces[singer];
    }

    /// @inheritdoc IProfileLogic
    function getFollowSBT(uint256 profileId) external view override returns (address) {
        return _getProfileStorage()._profileById[profileId].followSBT;
    }

    /// @inheritdoc IProfileLogic
    function getFollowCondition(uint256 profileId) external view override returns (address) {
        return _getProfileStorage()._profileById[profileId].followCondition;
    }

    /// @inheritdoc IProfileLogic
    function getHandle(uint256 profileId) external view override returns (string memory) {
        return _getProfileStorage()._profileById[profileId].handle;
    }

    /// @inheritdoc IProfileLogic
    function getProfileIdByHandle(string calldata handle) external view override returns (uint256) {
        return _getProfileStorage()._profileIdByHandleHash[keccak256(bytes(handle))];
    }

    /// @inheritdoc IProfileLogic
    function getProfileIdByAddress(address addr) external view override returns (uint256) {
        return _getProfileStorage()._profileIdByAddress[addr];
    }

    /// @inheritdoc IProfileLogic
    function getProfile(
        uint256 profileId
    ) external view override returns (OspDataTypes.ProfileStruct memory) {
        return _getProfileStorage()._profileById[profileId];
    }

    /// @inheritdoc IERC721Enumerable
    function tokenOfOwnerByIndex(
        address owner,
        uint256 index
    ) external view override returns (uint256) {
        require(index == 0 || balanceOf(owner) == 0, 'ERC721Enumerable: owner index out of bounds');
        return _getProfileStorage()._profileIdByAddress[owner];
    }

    /// @inheritdoc IERC721Enumerable
    function tokenByIndex(uint256 index) external view override returns (uint256) {
        return _getProfileStorage()._allTokens[index];
    }

    /**
     * @dev opensocial not support.
     */
    function getApproved(
        uint256 /*tokenId*/
    ) external pure override returns (address /*operator*/) {
        revert OspErrors.SBTTransferNotAllowed();
    }

    /**
     * @dev opensocial not support.
     */
    function isApprovedForAll(
        address /*owner*/,
        address /*operator*/
    ) external pure override returns (bool) {
        revert OspErrors.SBTTransferNotAllowed();
    }

    /*///////////////////////////////////////////////////////////////
                        Internal functions
    //////////////////////////////////////////////////////////////*/

    function _mint(address to) internal returns (uint256) {
        ProfileStorage storage profileStorage = _getProfileStorage();
        if (profileStorage._profileIdByAddress[to] != 0) revert OspErrors.SBTTokenAlreadyExists();
        uint256 tokenId = ++profileStorage._profileCounter;
        _addTokenToAllTokensEnumeration(tokenId);
        profileStorage._profileById[tokenId].owner = to;
        profileStorage._profileById[tokenId].mintTimestamp = uint96(block.timestamp);
        profileStorage._profileIdByAddress[to] = tokenId;
        emit Transfer(address(0), to, tokenId);
        return tokenId;
    }

    function _validateHandle(string calldata handle) internal pure returns (bytes32 hash) {
        bytes memory byteHandle = bytes(handle);
        if (
            byteHandle.length < Constants.MIN_HANDLE_LENGTH ||
            byteHandle.length > Constants.MAX_HANDLE_LENGTH
        ) revert OspErrors.HandleLengthInvalid();

        uint256 byteHandleLength = byteHandle.length;
        for (uint256 i; i < byteHandleLength; ) {
            if (
                (byteHandle[i] < '0' ||
                    byteHandle[i] > 'z' ||
                    (byteHandle[i] > '9' && byteHandle[i] < 'a')) && byteHandle[i] != '_'
            ) revert OspErrors.HandleContainsInvalidCharacters();
            unchecked {
                ++i;
            }
        }
        return keccak256(byteHandle);
    }

    function _createProfile(
        OspDataTypes.CreateProfileData calldata vars
    ) internal returns (uint256) {
        bytes32 handleHash = _validateHandle(vars.handle);
        mapping(bytes32 => uint256) storage _profileIdByHandleHash = _getProfileStorage()
            ._profileIdByHandleHash;
        if (_profileIdByHandleHash[handleHash] != 0) revert OspErrors.HandleTaken();
        uint256 profileId = _mint(msg.sender);
        OspDataTypes.ProfileStruct storage profileStruct = _getProfileStorage()._profileById[
            profileId
        ];
        profileStruct.handle = vars.handle;
        _profileIdByHandleHash[handleHash] = profileId;
        address followCondition;
        if (vars.followConditionInitCode.length != 0) {
            followCondition = _initFollowCondition(profileId, vars.followConditionInitCode);
            profileStruct.followCondition = followCondition;
        }
        if (vars.inviter != 0) {
            address inviterAddress = _getProfileStorage()._profileById[vars.inviter].owner;
            if (inviterAddress == address(0) || inviterAddress == msg.sender) {
                revert OspErrors.ProfileDoesNotExist();
            }
            profileStruct.inviter = vars.inviter;
        }
        emit OspEvents.ProfileCreated(
            profileId,
            msg.sender,
            vars.handle,
            followCondition,
            vars.inviter,
            vars.ctx,
            block.timestamp
        );
        return profileId;
    }

    function _setFollowCondition(
        uint256 profileId,
        bytes calldata followConditionInitCode
    ) internal {
        address followCondition = followConditionInitCode.length == 0
            ? address(0)
            : _initFollowCondition(profileId, followConditionInitCode);
        _getProfileStorage()._profileById[profileId].followCondition = followCondition;
        emit OspEvents.FollowConditionSet(profileId, followCondition, block.timestamp);
    }

    /**
     * @dev Private function to add a token to this extension's token tracking data structures.
     * @param tokenId uint256 ID of the token to be added to the tokens list
     */
    function _addTokenToAllTokensEnumeration(uint256 tokenId) private {
        ProfileStorage storage profileStorage = _getProfileStorage();
        profileStorage._allTokensIndex[tokenId] = profileStorage._allTokens.length;
        profileStorage._allTokens.push(tokenId);
    }

    function _initFollowCondition(
        uint256 profileId,
        bytes calldata initCode
    ) private returns (address followCondition) {
        followCondition = address(bytes20(initCode[:20]));
        _checkFollowCondition(followCondition);
        bytes memory initCallData = initCode[20:];
        IFollowCondition(followCondition).initializeFollowCondition(profileId, initCallData);
    }
}
合同源代码
文件 110 的 124:Proxy.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/Proxy.sol)

pragma solidity ^0.8.20;

/**
 * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM
 * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to
 * be specified by overriding the virtual {_implementation} function.
 *
 * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a
 * different contract through the {_delegate} function.
 *
 * The success and return data of the delegated call will be returned back to the caller of the proxy.
 */
abstract contract Proxy {
    /**
     * @dev Delegates the current call to `implementation`.
     *
     * This function does not return to its internal call site, it will return directly to the external caller.
     */
    function _delegate(address implementation) internal virtual {
        assembly {
            // Copy msg.data. We take full control of memory in this inline assembly
            // block because it will not return to Solidity code. We overwrite the
            // Solidity scratch pad at memory position 0.
            calldatacopy(0, 0, calldatasize())

            // Call the implementation.
            // out and outsize are 0 because we don't know the size yet.
            let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)

            // Copy the returned data.
            returndatacopy(0, 0, returndatasize())

            switch result
            // delegatecall returns 0 on error.
            case 0 {
                revert(0, returndatasize())
            }
            default {
                return(0, returndatasize())
            }
        }
    }

    /**
     * @dev This is a virtual function that should be overridden so it returns the address to which the fallback
     * function and {_fallback} should delegate.
     */
    function _implementation() internal view virtual returns (address);

    /**
     * @dev Delegates the current call to the address returned by `_implementation()`.
     *
     * This function does not return to its internal call site, it will return directly to the external caller.
     */
    function _fallback() internal virtual {
        _delegate(_implementation());
    }

    /**
     * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other
     * function in the contract matches the call data.
     */
    fallback() external payable virtual {
        _fallback();
    }
}
合同源代码
文件 111 的 124:ReferenceConditionBase.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

import {OspContext} from './OspContext.sol';
import {IReferenceCondition} from '../../interfaces/IReferenceCondition.sol';
import {IERC165} from '@openzeppelin/contracts/interfaces/IERC165.sol';

abstract contract ReferenceConditionBase is OspContext, IERC165, IReferenceCondition {
    constructor(address osp) OspContext(osp) {}

    function supportsInterface(bytes4 interfaceId) external pure override returns (bool) {
        return
            interfaceId == type(IReferenceCondition).interfaceId ||
            interfaceId == type(IERC165).interfaceId;
    }

    function initializeReferenceCondition(
        uint256 profileId,
        uint256 contendId,
        uint256 communityId,
        bytes calldata data
    ) external onlyOsp {
        _initializeReferenceCondition(profileId, communityId, contendId, data);
    }

    function processReactionReference(
        uint256 profileId,
        uint256 communityId,
        uint256 referencedProfileId,
        uint256 referencedContentId,
        bytes calldata data
    ) external payable onlyOsp {
        _processReactionReference(
            profileId,
            communityId,
            referencedProfileId,
            referencedContentId,
            data
        );
    }

    function _initializeReferenceCondition(
        uint256 profileId,
        uint256 contendId,
        uint256 communityId,
        bytes calldata data
    ) internal virtual;

    function _processReactionReference(
        uint256 profileId,
        uint256 communityId,
        uint256 referencedProfileId,
        uint256 referencedContentId,
        bytes calldata data
    ) internal virtual;
}
合同源代码
文件 112 的 124:RelationLogic.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

import '../../libraries/Constants.sol';
import '../../interfaces/IFollowCondition.sol';
import '../../interfaces/IFollowSBT.sol';
import '../../upgradeability/FollowSBTProxy.sol';
import '../../interfaces/IJoinCondition.sol';
import '../../interfaces/IJoinNFT.sol';
import './OspLogicBase.sol';
import './interfaces/IRelationLogic.sol';
import '@openzeppelin/contracts/utils/Strings.sol';
import '@openzeppelin/contracts/token/ERC721/IERC721.sol';

/**
 * @title RelationLogic
 * @author OpenSocial Protocol
 * @dev RelationLogic is logic contract that manages the relations of the protocol.
 * Relation includes the relationship between users and the relationship between users and the community
 */
contract RelationLogic is IRelationLogic, OspLogicBase {
    using Strings for uint256;

    modifier onlyJoinNFT(uint256 communityId) {
        _checkFromJoinNFT(communityId);
        _;
    }

    /*///////////////////////////////////////////////////////////////
                        Public functions
    //////////////////////////////////////////////////////////////*/

    /// @inheritdoc IRelationLogic
    function follow(
        OspDataTypes.FollowData calldata vars
    ) external payable override whenNotPaused returns (uint256) {
        return _follow(vars.profileId, vars.data, vars.ctx);
    }

    /// @inheritdoc IRelationLogic
    function batchFollow(
        OspDataTypes.BatchFollowData calldata vars
    ) external payable override whenNotPaused returns (uint256[] memory) {
        return _batchFollow(vars.profileIds, vars.datas, vars.values, vars.ctx);
    }

    /// @inheritdoc IRelationLogic
    function join(
        OspDataTypes.JoinData calldata vars
    ) external payable override whenNotPaused returns (uint256) {
        return _join(vars.communityId, vars.data, vars.ctx);
    }

    /// @inheritdoc IRelationLogic
    function batchJoin(
        OspDataTypes.BatchJoinData calldata vars
    ) external payable override whenNotPaused returns (uint256[] memory) {
        return _batchJoin(vars.communityIds, vars.datas, vars.values, vars.ctx);
    }

    /// @inheritdoc IRelationLogic
    function getFollowSBTURI(
        uint256 profileId,
        uint256 tokenId
    ) external view override returns (string memory) {
        string memory baseURI = _getGovernanceStorage()._baseURI;
        return
            bytes(baseURI).length > 0
                ? string(
                    abi.encodePacked(baseURI, profileId.toString(), '/follow/', tokenId.toString())
                )
                : tokenId.toString();
    }

    /// @inheritdoc IRelationLogic
    function getJoinNFTURI(
        uint256 communityId,
        uint256 tokenId
    ) external view override returns (string memory) {
        string memory baseURI = _getGovernanceStorage()._baseURI;
        return
            bytes(baseURI).length > 0
                ? string(
                    abi.encodePacked(baseURI, communityId.toString(), '/join/', tokenId.toString())
                )
                : tokenId.toString();
    }

    /// @inheritdoc IRelationLogic
    function isFollow(uint256 profileId, address addr) external view override returns (bool) {
        address followSBT = _getProfileStorage()._profileById[profileId].followSBT;
        return followSBT == address(0) ? false : IERC721(followSBT).balanceOf(addr) > 0;
    }

    /// @inheritdoc IRelationLogic
    function isJoin(uint256 communityId, address addr) external view override returns (bool) {
        address joinNFT = _getCommunityStorage()._communityById[communityId].joinNFT;
        try IERC721(joinNFT).balanceOf(addr) returns (uint256 balance) {
            return balance > 0;
        } catch {
            return false;
        }
    }

    /// @inheritdoc IRelationLogic
    function hasCommunityRole(
        uint256 communityId,
        uint256 role,
        address account
    ) external view override returns (bool) {
        address joinNFT = _getCommunityStorage()._communityById[communityId].joinNFT;
        return IJoinNFT(joinNFT).hasRole(role, account);
    }

    /// @inheritdoc IRelationLogic
    function getCommunityRole(
        uint256 communityId,
        address account
    ) external view returns (uint256) {
        address joinNFT = _getCommunityStorage()._communityById[communityId].joinNFT;
        return IJoinNFT(joinNFT).getRole(account);
    }

    /// @inheritdoc IRelationLogic
    function getCommunityMemberLevel(
        uint256 communityId,
        address account
    ) external view returns (uint256) {
        address joinNFT = _getCommunityStorage()._communityById[communityId].joinNFT;
        return IJoinNFT(joinNFT).getMemberLevel(account);
    }

    /// @inheritdoc IRelationLogic
    function isCommunityBlock(uint256 communityId, address account) external view returns (bool) {
        address joinNFT = _getCommunityStorage()._communityById[communityId].joinNFT;
        return IJoinNFT(joinNFT).isBlock(account);
    }

    /// @inheritdoc IRelationLogic
    function emitFollowSBTTransferEvent(
        uint256 profileId,
        uint256 followSBTId,
        address from,
        address to
    ) external override {
        if (_getProfileStorage()._profileById[profileId].followSBT != msg.sender) {
            revert OspErrors.NotFollowSBT();
        }
        emit OspEvents.FollowSBTTransferred(profileId, followSBTId, from, to, block.timestamp);
    }

    /// @inheritdoc IRelationLogic
    function emitJoinNFTTransferEvent(
        uint256 communityId,
        uint256 joinNFTId,
        address from,
        address to
    ) external override {
        if (_getCommunityStorage()._communityById[communityId].joinNFT != msg.sender) {
            revert OspErrors.NotJoinNFT();
        }
        address joinCondition = _getCommunityStorage()._communityById[communityId].joinCondition;
        if (joinCondition != address(0)) {
            IJoinCondition(joinCondition).processTransferJoinNFT(communityId, joinNFTId, from, to);
        }
        emit OspEvents.JoinNFTTransferred(communityId, joinNFTId, from, to, block.timestamp);
    }

    /// @inheritdoc IRelationLogic
    function emitJoinNFTRoleChangedEvent(
        uint256 communityId,
        address sender,
        address account,
        uint256 role
    ) external override onlyJoinNFT(communityId) {
        emit OspEvents.JoinNFTRoleChanged(communityId, sender, account, role, block.timestamp);
    }

    /// @inheritdoc IRelationLogic
    function emitJoinNFTAccountBlockedEvent(
        uint256 communityId,
        address sender,
        address account,
        bool isBlock
    ) external override onlyJoinNFT(communityId) {
        emit OspEvents.JoinNFTAccountBlocked(
            communityId,
            sender,
            account,
            isBlock,
            block.timestamp
        );
    }

    /// @inheritdoc IRelationLogic
    function emitJoinNFTAccountLevelChangedEvent(
        uint256 communityId,
        address sender,
        uint256 tokenId,
        address account,
        uint256 level
    ) external override onlyJoinNFT(communityId) {
        address joinNFT = _getCommunityStorage()._communityById[communityId].joinNFT;
        emit OspEvents.JoinNFTAccountLevelChanged(
            communityId,
            tokenId,
            sender,
            account,
            joinNFT,
            level,
            block.timestamp
        );
    }

    /*///////////////////////////////////////////////////////////////
                        Internal functions
    //////////////////////////////////////////////////////////////*/

    function _follow(
        uint256 targetProfileId,
        bytes calldata followConditionData,
        bytes calldata ctx
    ) internal returns (uint256 tokenId) {
        uint256 profileId = _validateHasProfile(msg.sender);
        tokenId = _executeFollow(targetProfileId, followConditionData, msg.value);
        emit OspEvents.Followed(
            msg.sender,
            profileId,
            targetProfileId,
            followConditionData,
            tokenId,
            ctx,
            block.timestamp
        );
    }

    /**
     * @dev Follows the given profiles, executing the necessary logic and condition calls before minting the follow
     * NFT(s) to the follower.
     *
     * @param targetProfileIds The array of profile token IDs to follow.
     * @param followConditionDatas The array of follow condition data parameters to pass to each profile's follow condition.
     *
     * @return uint256[] An array of integers representing the minted follow NFTs token IDs.
     */
    function _batchFollow(
        uint256[] calldata targetProfileIds,
        bytes[] calldata followConditionDatas,
        uint256[] calldata values,
        bytes calldata ctx
    ) internal returns (uint256[] memory) {
        uint256 profileId = _validateHasProfile(msg.sender);
        uint256 length = targetProfileIds.length;
        if (length != followConditionDatas.length || length != values.length)
            revert OspErrors.ArrayMismatch();
        uint256[] memory tokenIds = new uint256[](length);
        uint256 batchValue;
        for (uint256 i; i < length; ) {
            batchValue += values[i];
            tokenIds[i] = _executeFollow(targetProfileIds[i], followConditionDatas[i], values[i]);
            unchecked {
                ++i;
            }
        }
        if (batchValue > msg.value) {
            revert OspErrors.InvalidValue();
        }
        emit OspEvents.BatchFollowed(
            msg.sender,
            profileId,
            targetProfileIds,
            followConditionDatas,
            tokenIds,
            ctx,
            block.timestamp
        );
        return tokenIds;
    }

    function _executeFollow(
        uint256 profileId,
        bytes calldata followConditionData,
        uint256 value
    ) internal returns (uint256 tokenId) {
        address owner = _getProfileStorage()._profileById[profileId].owner;
        if (owner == address(0)) revert OspErrors.TokenDoesNotExist();
        if (owner == msg.sender) revert OspErrors.InvalidProfileId();
        address followCondition = _getProfileStorage()._profileById[profileId].followCondition;
        address followSBT = _getProfileStorage()._profileById[profileId].followSBT;

        if (followSBT == address(0)) {
            followSBT = _deployFollowSBT(profileId);
            _getProfileStorage()._profileById[profileId].followSBT = followSBT;
        }

        tokenId = IFollowSBT(followSBT).mint(msg.sender);

        if (followCondition != address(0)) {
            IFollowCondition(followCondition).processFollow{value: value}(
                msg.sender,
                profileId,
                followConditionData
            );
        }
    }

    /**
     * @dev Deploys the given profile's Follow SBT contract.
     *
     * @param profileId The token ID of the profile which Follow SBT should be deployed.
     *
     * @return address The address of the deployed Follow SBT contract.
     */
    function _deployFollowSBT(uint256 profileId) internal returns (address) {
        string memory idStr = profileId.toString();
        string memory name = string(abi.encodePacked(idStr, Constants.FOLLOW_NFT_NAME_SUFFIX));
        string memory symbol = string(abi.encodePacked(idStr, Constants.FOLLOW_NFT_SYMBOL_SUFFIX));

        bytes memory functionData = abi.encodeWithSelector(
            IFollowSBT.initialize.selector,
            profileId,
            name,
            symbol
        );
        address followSBT = address(new FollowSBTProxy(functionData));
        emit OspEvents.FollowSBTDeployed(profileId, followSBT, block.timestamp);
        return followSBT;
    }

    /**
     * @dev Joins the given community, executing the necessary logic and condition calls before minting the join
     * NFT to the follower.
     *
     * @param communityId The token ID of the community to join.
     * @param joinConditionData The join condition data parameters to pass to the community's join condition.
     *
     * @return tokenId An integer representing the minted join NFTs token ID.
     */
    function _join(
        uint256 communityId,
        bytes calldata joinConditionData,
        bytes calldata ctx
    ) internal returns (uint256 tokenId) {
        uint256 profileId = _validateHasProfile(msg.sender);
        tokenId = _executeJoin(communityId, joinConditionData, msg.value);
        emit OspEvents.Joined(
            msg.sender,
            profileId,
            communityId,
            joinConditionData,
            tokenId,
            ctx,
            block.timestamp
        );
    }

    /**
     * @dev Joins the given communities, executing the necessary logic and condition calls before minting the join
     * NFT(s) to the follower.
     *
     * @param communityIds The array of community token IDs to join.
     * @param joinConditionDatas The array of join condition data parameters to pass to each community's join condition.
     * @param values The array of values to send to each community's join condition.
     *
     * @return uint256[] An array of integers representing the minted join NFTs token IDs.
     */
    function _batchJoin(
        uint256[] calldata communityIds,
        bytes[] calldata joinConditionDatas,
        uint256[] calldata values,
        bytes calldata ctx
    ) internal returns (uint256[] memory) {
        uint256 profileId = _validateHasProfile(msg.sender);
        uint256 length = communityIds.length;
        if (length != joinConditionDatas.length || length != values.length)
            revert OspErrors.ArrayMismatch();
        uint256[] memory tokenIds = new uint256[](length);
        {
            uint256 batchValue;
            for (uint256 i; i < length; ) {
                batchValue += values[i];
                tokenIds[i] = _executeJoin(communityIds[i], joinConditionDatas[i], values[i]);
                unchecked {
                    ++i;
                }
            }
            if (batchValue > msg.value) {
                revert OspErrors.InvalidValue();
            }
        }
        emit OspEvents.BatchJoined(
            msg.sender,
            profileId,
            communityIds,
            joinConditionDatas,
            tokenIds,
            ctx,
            block.timestamp
        );
        return tokenIds;
    }

    /**
     * @dev Executes the join logic for a given community, minting the join NFT to the follower and processing the
     * community's join condition logic (if any).
     *
     * @param communityId The token ID of the community to join.
     * @param joinConditionData The join condition data parameters to pass to the community's join condition.
     * @param value The value to send to the community's join condition.
     *
     * @return tokenId An integer representing the minted join NFTs token ID.
     */
    function _executeJoin(
        uint256 communityId,
        bytes calldata joinConditionData,
        uint256 value
    ) internal returns (uint256 tokenId) {
        OspDataTypes.CommunityStruct memory community = _getCommunityStorage()._communityById[
            communityId
        ];
        if (community.joinNFT == address(0)) revert OspErrors.InvalidCommunityId();
        tokenId = IJoinNFT(community.joinNFT).mint(msg.sender);
        if (community.joinCondition != address(0)) {
            IJoinCondition(community.joinCondition).processJoin{value: value}(
                msg.sender,
                communityId,
                joinConditionData
            );
        }
    }

    function _checkFromJoinNFT(uint256 communityId) internal {
        if (_getCommunityStorage()._communityById[communityId].joinNFT != msg.sender) {
            revert OspErrors.NotJoinNFT();
        }
    }
}
合同源代码
文件 113 的 124:RoleActivityCond.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

import {ActivityConditionBase} from 'contracts/core/base/ActivityConditionBase.sol';
import {IRoleActivityCond} from './interfaces/IRoleActivityCond.sol';
import {CondErrors} from '../libraries/CondErrors.sol';

import {IRelationLogic} from 'contracts/core/logics/interfaces/IRelationLogic.sol';

contract RoleActivityCond is ActivityConditionBase, IRoleActivityCond {
    mapping(uint256 => uint256) internal _roleLimitByCommunity;

    constructor(
        address osp,
        address compositeActivityCondition
    ) ActivityConditionBase(osp, compositeActivityCondition) {}

    function _initializeCommunityActivityCondition(
        uint256 communityId,
        bytes calldata data
    ) internal override {
        uint256 accessLimit = abi.decode(data, (uint256));
        _roleLimitByCommunity[communityId] = accessLimit;
    }

    function _processActivity(
        uint256 communityId,
        address profileOwner,
        uint256 profileId
    ) internal override {
        if (
            IRelationLogic(OSP).hasCommunityRole(
                communityId,
                _roleLimitByCommunity[communityId],
                profileOwner
            )
        ) return;
        revert CondErrors.AccessDenied();
    }

    // @inheritdoc IRoleActivityCond
    function getRoleLimit(uint256 communityId) external view returns (uint256 roleLimit) {
        roleLimit = _roleLimitByCommunity[communityId];
    }

    // @inheritdoc IERC165
    function supportsInterface(bytes4 interfaceId) public pure override returns (bool) {
        return
            ActivityConditionBase.supportsInterface(interfaceId) ||
            interfaceId == type(IRoleActivityCond).interfaceId;
    }
}
合同源代码
文件 114 的 124:RoleViewPrivacyCond.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.20;

import {ViewPrivacyConditionBase} from 'contracts/core/base/ViewPrivacyConditionBase.sol';
import {IRoleViewPrivacyCond} from './interfaces/IRoleViewPrivacyCond.sol';

import {IRelationLogic} from 'contracts/core/logics/interfaces/IRelationLogic.sol';

contract RoleViewPrivacyCond is ViewPrivacyConditionBase, IRoleViewPrivacyCond {
    mapping(uint256 => uint256) internal _roleLimitByCommunity;

    constructor(
        address osp,
        address compositeCondition
    ) ViewPrivacyConditionBase(osp, compositeCondition) {}

    function _initializeCommunityViewPrivacyCondition(
        uint256 communityId,
        bytes calldata data
    ) internal override {
        uint256 accessLimit = abi.decode(data, (uint256));
        _roleLimitByCommunity[communityId] = accessLimit;
    }

    function _isViewPrivacyAllowed(
        uint256 communityId,
        address profileOwner,
        uint256 profileId
    ) internal view override returns (bool) {
        if (
            IRelationLogic(OSP).hasCommunityRole(
                communityId,
                _roleLimitByCommunity[communityId],
                profileOwner
            )
        ) return true;
        return false;
    }

    // @inheritdoc IRoleViewPrivacyCond
    function getRoleLimit(uint256 communityId) external view override returns (uint256 roleLimit) {
        return _roleLimitByCommunity[communityId];
    }

    // @inheritdoc IERC165
    function supportsInterface(bytes4 interfaceId) public pure override returns (bool) {
        return
            ViewPrivacyConditionBase.supportsInterface(interfaceId) ||
            interfaceId == type(IRoleViewPrivacyCond).interfaceId;
    }
}
合同源代码
文件 115 的 124:SafeERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../utils/Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    using Address for address;

    /**
     * @dev An operation with an ERC20 token failed.
     */
    error SafeERC20FailedOperation(address token);

    /**
     * @dev Indicates a failed `decreaseAllowance` request.
     */
    error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);

    /**
     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
    }

    /**
     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
     */
    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
    }

    /**
     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        forceApprove(token, spender, oldAllowance + value);
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
     * value, non-reverting calls are assumed to be successful.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
        unchecked {
            uint256 currentAllowance = token.allowance(address(this), spender);
            if (currentAllowance < requestedDecrease) {
                revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
            }
            forceApprove(token, spender, currentAllowance - requestedDecrease);
        }
    }

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
     * to be set to zero before setting it to a non-zero value, such as USDT.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));

        if (!_callOptionalReturnBool(token, approvalCall)) {
            _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
            _callOptionalReturn(token, approvalCall);
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data);
        if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
        // and not revert is the subcall reverts.

        (bool success, bytes memory returndata) = address(token).call(data);
        return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
    }
}
合同源代码
文件 116 的 124:SignatureChecker.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/SignatureChecker.sol)

pragma solidity ^0.8.20;

import {ECDSA} from "./ECDSA.sol";
import {IERC1271} from "../../interfaces/IERC1271.sol";

/**
 * @dev Signature verification helper that can be used instead of `ECDSA.recover` to seamlessly support both ECDSA
 * signatures from externally owned accounts (EOAs) as well as ERC1271 signatures from smart contract wallets like
 * Argent and Safe Wallet (previously Gnosis Safe).
 */
library SignatureChecker {
    /**
     * @dev Checks if a signature is valid for a given signer and data hash. If the signer is a smart contract, the
     * signature is validated against that smart contract using ERC1271, otherwise it's validated using `ECDSA.recover`.
     *
     * NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus
     * change through time. It could return true at block N and false at block N+1 (or the opposite).
     */
    function isValidSignatureNow(address signer, bytes32 hash, bytes memory signature) internal view returns (bool) {
        (address recovered, ECDSA.RecoverError error, ) = ECDSA.tryRecover(hash, signature);
        return
            (error == ECDSA.RecoverError.NoError && recovered == signer) ||
            isValidERC1271SignatureNow(signer, hash, signature);
    }

    /**
     * @dev Checks if a signature is valid for a given signer and data hash. The signature is validated
     * against the signer smart contract using ERC1271.
     *
     * NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus
     * change through time. It could return true at block N and false at block N+1 (or the opposite).
     */
    function isValidERC1271SignatureNow(
        address signer,
        bytes32 hash,
        bytes memory signature
    ) internal view returns (bool) {
        (bool success, bytes memory result) = signer.staticcall(
            abi.encodeCall(IERC1271.isValidSignature, (hash, signature))
        );
        return (success &&
            result.length >= 32 &&
            abi.decode(result, (bytes32)) == bytes32(IERC1271.isValidSignature.selector));
    }
}
合同源代码
文件 117 的 124:SignedMath.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SignedMath.sol)

pragma solidity ^0.8.20;

/**
 * @dev Standard signed math utilities missing in the Solidity language.
 */
library SignedMath {
    /**
     * @dev Returns the largest of two signed numbers.
     */
    function max(int256 a, int256 b) internal pure returns (int256) {
        return a > b ? a : b;
    }

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

    /**
     * @dev Returns the average of two signed numbers without overflow.
     * The result is rounded towards zero.
     */
    function average(int256 a, int256 b) internal pure returns (int256) {
        // Formula from the book "Hacker's Delight"
        int256 x = (a & b) + ((a ^ b) >> 1);
        return x + (int256(uint256(x) >> 255) & (a ^ b));
    }

    /**
     * @dev Returns the absolute unsigned value of a signed value.
     */
    function abs(int256 n) internal pure returns (uint256) {
        unchecked {
            // must be unchecked in order to support `n = type(int256).min`
            return uint256(n >= 0 ? n : -n);
        }
    }
}
合同源代码
文件 118 的 124:SlotNFTCommunityCond.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

import {CondErrors} from '../libraries/CondErrors.sol';
import {IERC721} from '@openzeppelin/contracts/token/ERC721/IERC721.sol';
import {CommunityCondBase} from '../../base/CommunityCondBase.sol';
import {CondDataTypes} from '../libraries/CondDataTypes.sol';

/**
 * @title SlotNFTCommunityCond
 * @author OpenSocial Protocol
 *
 * @dev This contract specifies that holding slotNFT can create communities.
 * Each slotNFT can only be used once.
 */
contract SlotNFTCommunityCond is CommunityCondBase {
    event SlotNFTCondDataSet(address indexed slot, uint256 minHandleLength, uint256 timestamp);
    event SlotNFTUsed(
        address indexed to,
        address indexed slot,
        uint256 indexed tokenId,
        string handle,
        uint256 timestamp
    );

    constructor(address osp) CommunityCondBase(osp) {}

    mapping(address => mapping(uint256 => bool)) _slotNFTUsed;

    mapping(address => CondDataTypes.SlotNFTCondData) _slotNFTCondData;

    /**
     * @dev process create community,if the slotNFT is used, revert.
     */
    function _processCreateCommunity(
        address to,
        string calldata handle,
        bytes calldata data
    ) internal override nonPayable {
        (address slot, uint256 tokenId) = abi.decode(data, (address, uint256));
        _validateSlotNFT(to, slot, tokenId);
        uint256 len = bytes(handle).length;
        if (len < _slotNFTCondData[slot].minHandleLength) {
            revert CondErrors.HandleLengthNotEnough();
        }
        _slotNFTUsed[slot][tokenId] = true;
        emit SlotNFTUsed(to, slot, tokenId, handle, block.timestamp);
    }

    /**
     * @dev Set slotNFT condition data.
     * @param slot NFT contract address.
     * @param whitelist Whether the slotNFT is whitelisted.
     * @param minHandleLength Minimum handle length to create a community.
     */
    function setSlotNFTCondData(
        address slot,
        bool whitelist,
        uint256 minHandleLength
    ) external onlyOperation {
        if (slot == address(0) || minHandleLength <= 0) {
            revert CondErrors.InitParamsInvalid();
        }
        _slotNFTCondData[slot] = CondDataTypes.SlotNFTCondData({
            whitelist: whitelist,
            minHandleLength: minHandleLength
        });
        emit SlotNFTCondDataSet(slot, minHandleLength, block.timestamp);
    }

    function getSlotNFTCondData(
        address slot
    ) external view returns (CondDataTypes.SlotNFTCondData memory) {
        return _slotNFTCondData[slot];
    }

    /**
     * @dev Check if a slotNFT is usable.
     * @param slot NFT contract address.
     * @param addr Creator address.
     * @param tokenId NFT token id.
     */
    function isSlotNFTUsable(
        address slot,
        address addr,
        uint256 tokenId
    ) external view returns (bool) {
        return
            _slotNFTCondData[slot].whitelist &&
            !_slotNFTUsed[slot][tokenId] &&
            IERC721(slot).ownerOf(tokenId) == addr;
    }

    function _validateSlotNFT(address addr, address slot, uint256 tokenId) internal view {
        if (!_slotNFTCondData[slot].whitelist) {
            revert CondErrors.SlotNFTNotWhitelisted();
        }
        if (IERC721(slot).ownerOf(tokenId) != addr) {
            revert CondErrors.NotSlotNFTOwner();
        }
        if (_slotNFTUsed[slot][tokenId]) {
            revert CondErrors.SlotNFTAlreadyUsed();
        }
    }
}
合同源代码
文件 119 的 124:StorageSlot.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/StorageSlot.sol)
// This file was procedurally generated from scripts/generate/templates/StorageSlot.js.

pragma solidity ^0.8.20;

/**
 * @dev Library for reading and writing primitive types to specific storage slots.
 *
 * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
 * This library helps with reading and writing to such slots without the need for inline assembly.
 *
 * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
 *
 * Example usage to set ERC1967 implementation slot:
 * ```solidity
 * contract ERC1967 {
 *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
 *
 *     function _getImplementation() internal view returns (address) {
 *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
 *     }
 *
 *     function _setImplementation(address newImplementation) internal {
 *         require(newImplementation.code.length > 0);
 *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
 *     }
 * }
 * ```
 */
library StorageSlot {
    struct AddressSlot {
        address value;
    }

    struct BooleanSlot {
        bool value;
    }

    struct Bytes32Slot {
        bytes32 value;
    }

    struct Uint256Slot {
        uint256 value;
    }

    struct StringSlot {
        string value;
    }

    struct BytesSlot {
        bytes value;
    }

    /**
     * @dev Returns an `AddressSlot` with member `value` located at `slot`.
     */
    function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
     */
    function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
     */
    function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
     */
    function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `StringSlot` with member `value` located at `slot`.
     */
    function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `StringSlot` representation of the string storage pointer `store`.
     */
    function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := store.slot
        }
    }

    /**
     * @dev Returns an `BytesSlot` with member `value` located at `slot`.
     */
    function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
     */
    function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := store.slot
        }
    }
}
合同源代码
文件 120 的 124:Strings.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Strings.sol)

pragma solidity ^0.8.20;

import {Math} from "./math/Math.sol";
import {SignedMath} from "./math/SignedMath.sol";

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

    /**
     * @dev The `value` string doesn't fit in the specified `length`.
     */
    error StringsInsufficientHexLength(uint256 value, uint256 length);

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

    /**
     * @dev Converts a `int256` to its ASCII `string` decimal representation.
     */
    function toStringSigned(int256 value) internal pure returns (string memory) {
        return string.concat(value < 0 ? "-" : "", toString(SignedMath.abs(value)));
    }

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

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

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

    /**
     * @dev Returns true if the two strings are equal.
     */
    function equal(string memory a, string memory b) internal pure returns (bool) {
        return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b));
    }
}
合同源代码
文件 121 的 124:ViewPrivacyConditionBase.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

import {IViewPrivacyCondition} from 'contracts/interfaces/IViewPrivacyCondition.sol';
import {IERC165} from '@openzeppelin/contracts/interfaces/IERC165.sol';
import {ConditionContext} from './ConditionContext.sol';

abstract contract ViewPrivacyConditionBase is ConditionContext, IERC165, IViewPrivacyCondition {
    constructor(
        address osp,
        address compositeCondition
    ) ConditionContext(osp, compositeCondition) {}

    function initializeCommunityViewPrivacyCondition(
        uint256 communityId,
        bytes calldata data
    ) external onlyOspOrCompositeCondition {
        _initializeCommunityViewPrivacyCondition(communityId, data);
    }

    function isViewPrivacyAllowed(
        uint256 communityId,
        address profileOwner,
        uint256 profileId
    ) external view onlyOspOrCompositeCondition returns (bool) {
        return _isViewPrivacyAllowed(communityId, profileOwner, profileId);
    }

    function _initializeCommunityViewPrivacyCondition(
        uint256 communityId,
        bytes calldata data
    ) internal virtual;

    function _isViewPrivacyAllowed(
        uint256 communityId,
        address profileOwner,
        uint256 profileId
    ) internal view virtual returns (bool);

    function supportsInterface(bytes4 interfaceId) public pure virtual override returns (bool) {
        return
            interfaceId == type(IViewPrivacyCondition).interfaceId ||
            interfaceId == type(IERC165).interfaceId;
    }
}
合同源代码
文件 122 的 124:VoteReaction.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

import {OpenReactionBase} from '../base/OpenReactionBase.sol';
import {OspErrors} from '../../libraries/OspErrors.sol';

contract VoteReaction is OpenReactionBase {
    constructor(address osp) OpenReactionBase(osp) {}

    enum Vote {
        Cancel,
        Up,
        Down
    }

    function _processReaction(
        uint256 /*profileId*/,
        uint256 /*referencedProfileId*/,
        uint256 /*referencedContentId*/,
        bytes calldata data
    ) internal view override {
        abi.decode(data, (Vote));
    }
}
合同源代码
文件 123 的 124:WhitelistAddressCommunityCond.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

import {CondErrors} from '../libraries/CondErrors.sol';
import {CommunityCondBase} from '../../base/CommunityCondBase.sol';

/**
 * @title WhitelistAddressCommunityCond
 * @author OpenSocial Protocol
 *
 * @dev This contract specifies that whitelisted address can create communities.
 *
 */
contract WhitelistAddressCommunityCond is CommunityCondBase {
    event MaxCreationNumberSet(address indexed to, uint256 maxCreationNumber, uint256 timestamp);

    constructor(address osp) CommunityCondBase(osp) {}

    mapping(address => uint256) public maxCreationNumber;
    mapping(address => uint256) public creationCount;

    /**
     *  @dev process create community,if the address is not whitelisted, revert.
     */
    function _processCreateCommunity(
        address to,
        string calldata /*handle*/,
        bytes calldata /*data*/
    ) internal override nonPayable {
        if (maxCreationNumber[to] <= creationCount[to]) {
            revert CondErrors.NotWhitelisted();
        }
        creationCount[to]++;
    }

    /**
     *  @dev set the max creation number of an address, only openSocial governance can call this function.
     */
    function setMaxCreationNumber(address to, uint256 _maxCreationNumber) external onlyOperation {
        maxCreationNumber[to] = _maxCreationNumber;
        emit MaxCreationNumberSet(to, _maxCreationNumber, block.timestamp);
    }

    /**
     *  @return The number of communities allowed to be created.
     */
    function allowedCreationNumber(address to) external view returns (uint256) {
        return
            maxCreationNumber[to] > creationCount[to]
                ? maxCreationNumber[to] - creationCount[to]
                : 0;
    }
}
合同源代码
文件 124 的 124:draft-IERC6093.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC6093.sol)
pragma solidity ^0.8.20;

/**
 * @dev Standard ERC20 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC20 tokens.
 */
interface IERC20Errors {
    /**
     * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param balance Current balance for the interacting account.
     * @param needed Minimum amount required to perform a transfer.
     */
    error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC20InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC20InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.
     * @param spender Address that may be allowed to operate on tokens without being their owner.
     * @param allowance Amount of tokens a `spender` is allowed to operate with.
     * @param needed Minimum amount required to perform a transfer.
     */
    error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC20InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `spender` to be approved. Used in approvals.
     * @param spender Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC20InvalidSpender(address spender);
}

/**
 * @dev Standard ERC721 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC721 tokens.
 */
interface IERC721Errors {
    /**
     * @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in EIP-20.
     * Used in balance queries.
     * @param owner Address of the current owner of a token.
     */
    error ERC721InvalidOwner(address owner);

    /**
     * @dev Indicates a `tokenId` whose `owner` is the zero address.
     * @param tokenId Identifier number of a token.
     */
    error ERC721NonexistentToken(uint256 tokenId);

    /**
     * @dev Indicates an error related to the ownership over a particular token. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param tokenId Identifier number of a token.
     * @param owner Address of the current owner of a token.
     */
    error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC721InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC721InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     * @param tokenId Identifier number of a token.
     */
    error ERC721InsufficientApproval(address operator, uint256 tokenId);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC721InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC721InvalidOperator(address operator);
}

/**
 * @dev Standard ERC1155 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC1155 tokens.
 */
interface IERC1155Errors {
    /**
     * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param balance Current balance for the interacting account.
     * @param needed Minimum amount required to perform a transfer.
     * @param tokenId Identifier number of a token.
     */
    error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC1155InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC1155InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     * @param owner Address of the current owner of a token.
     */
    error ERC1155MissingApprovalForAll(address operator, address owner);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC1155InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC1155InvalidOperator(address operator);

    /**
     * @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.
     * Used in batch transfers.
     * @param idsLength Length of the array of token identifiers
     * @param valuesLength Length of the array of token amounts
     */
    error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
}
设置
{
  "compilationTarget": {
    "contracts/upgradeability/OspRouterImmutable.sol": "OspRouterImmutable"
  },
  "evmVersion": "shanghai",
  "libraries": {},
  "metadata": {
    "bytecodeHash": "ipfs"
  },
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "remappings": []
}
ABI
[{"inputs":[{"internalType":"address","name":"admin_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"previousAdmin","type":"address"},{"indexed":false,"internalType":"address","name":"newAdmin","type":"address"}],"name":"AdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"pendingAdmin","type":"address"},{"indexed":false,"internalType":"uint256","name":"pendingAdminTimestamp","type":"uint256"}],"name":"ChangeAdminStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"pendingAdmin","type":"address"}],"name":"PendingAdminRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes4","name":"functionSelector","type":"bytes4"},{"indexed":true,"internalType":"address","name":"routerAddress","type":"address"}],"name":"RouterAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes4","name":"functionSelector","type":"bytes4"},{"indexed":true,"internalType":"address","name":"routerAddress","type":"address"}],"name":"RouterRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes4","name":"functionSelector","type":"bytes4"},{"indexed":true,"internalType":"string","name":"functionSignature","type":"string"},{"indexed":true,"internalType":"address","name":"routerAddress","type":"address"}],"name":"RouterSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes4","name":"functionSelector","type":"bytes4"},{"indexed":true,"internalType":"address","name":"oldRouterAddress","type":"address"},{"indexed":true,"internalType":"address","name":"newRouterAddress","type":"address"}],"name":"RouterUpdated","type":"event"},{"stateMutability":"payable","type":"fallback"},{"inputs":[{"internalType":"bytes4","name":"_selector","type":"bytes4"}],"name":"_getRouterForFunction","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptAdminRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes4","name":"functionSelector","type":"bytes4"},{"internalType":"address","name":"routerAddress","type":"address"},{"internalType":"string","name":"functionSignature","type":"string"}],"internalType":"struct IRouter.Router","name":"_router","type":"tuple"}],"name":"addRouter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newAdmin","type":"address"}],"name":"changeAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getAdmin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_routerAddress","type":"address"}],"name":"getAllFunctionsOfRouter","outputs":[{"internalType":"bytes4[]","name":"registered","type":"bytes4[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAllRouters","outputs":[{"components":[{"internalType":"bytes4","name":"functionSelector","type":"bytes4"},{"internalType":"address","name":"routerAddress","type":"address"},{"internalType":"string","name":"functionSignature","type":"string"}],"internalType":"struct IRouter.Router[]","name":"registered","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPendingAdmin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPendingAdminTimestamp","outputs":[{"internalType":"uint96","name":"","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"_selector","type":"bytes4"}],"name":"getRouterForFunction","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes[]","name":"data","type":"bytes[]"}],"name":"multicall","outputs":[{"internalType":"bytes[]","name":"results","type":"bytes[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"selector","type":"bytes4"},{"internalType":"string","name":"functionSignature","type":"string"}],"name":"removeRouter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"revokePendingAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"bytes4","name":"functionSelector","type":"bytes4"},{"internalType":"address","name":"routerAddress","type":"address"},{"internalType":"string","name":"functionSignature","type":"string"}],"internalType":"struct IRouter.Router","name":"_router","type":"tuple"}],"name":"updateRouter","outputs":[],"stateMutability":"nonpayable","type":"function"}]