ETH Price: $2,427.09 (+0.48%)

Transaction Decoder

Block:
22246788 at Apr-11-2025 04:06:59 PM +UTC
Transaction Fee:
0.00019880087190637 ETH $0.48
Gas Used:
56,869 Gas / 3.49576873 Gwei

Account State Difference:

  Address   Before After State Difference Code
0x49e68883...802737ffD
0.049260255733262385 Eth
Nonce: 9
0.049061454861356015 Eth
Nonce: 10
0.00019880087190637
0x6120fA4b...303D0ff06
(beaverbuild)
12.784340184299899367 Eth12.784395347229899367 Eth0.00005516293

Execution Trace

ERC20AntiBot.setExempt( _tokenAddress=0x8546B0f5930721193bf26d7EEB1FBd0D1959562b, _traderAddress=0x3E77Df571E26A7Bd1CbB2F59B21E609d2819C0f8, _exempt=True )
  • Indexer.getProjectAddressOwner( _projectAddress=0x8546B0f5930721193bf26d7EEB1FBd0D1959562b ) => ( 0x49e68883Fabbf125BdCe306B077f6A2802737ffD )
    setExempt[ERC20AntiBot (ln:94)]
    File 1 of 2: ERC20AntiBot
    // SPDX-License-Identifier: UNLICENSED
    pragma solidity ^0.8.24;
    /*
                                                                                                        
                        &&&&&& &&&&&                                        &%                          
                         &&&&&&&&&&&&&&&                               && &&&&&&&&&&%                   
                         &&&&&&&      &&&&&                          #&&&&&&&&&&&&&                     
                         &&&&&  &&&&&&&   &&                      &&&&    &%  &&&&&&&                   
                            &&. &&&&&&&&&&                        &  %&&&&&&&  &&&&                     
                            &&&&  *&&&&&&&&&                       &&&&&&&&&  &&&&                      
                               .&&&   &&&&&&&&                   &&&&&&&&   &&&                         
                                        .&&&&&&&               &&&&&&&   &&&%                           
                                           &&&&&&             &&&&&&                                    
                                             &&&&&          /&&&&.                                      
                                               &&&&  %&&&  #&&&,                                        
                                       &&&&(     &&&&&&&&&&&&&                                          
                                   &&&&&&&&&&&&&&&&&&&&&&&&&&&&   &&&&&&&&&&                            
                                 &&&%        &&&&&&&&&&&&&&&&&&&&&&&&&     &&&                          
                                &&&    &&&*    &&&&&&&&&&&&&&&&&&&&&         &&*                        
                               &&&   .&&&&&&   &&&&&&&&&&&&&&&&&&&&   &&&&&   &&                        
                               &&&   #&&&&&&   &&&&&&&&&&&&&&&&&&&&&  &&&&&   &&                        
                                &&&    &&&&    &&&&&&&&&&&&&&&&&&&&&&  #&&   &&&                        
                                 &&&         &&&&&&&&&&&&&&&&&&&&&&&&,     *&&&                         
                                  (&&&&&&&&&&&&&&&&&&&&          &&&&&&&&&&&%                           
                                      &&&&&&&&&&&&&&&&&          &&&&&                                  
                                        &&&&&&&&&&&&&&&&&&    %&&&&&&&                                  
                                        &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&#                                 
                                          &&&&&&&&&&&&&&&&&&&&&&&&&&&&                                  
                                               &&&&&&&&&&&&&&&&&&&&                                     
    */
    /*
     * ERC20AntiBot contract
     * This contract is used to control bad actors using bots on trades
     */
    /// @title ERC20AntiBot
    /// @author Smithii
    import {IERC20AntiBot} from "./interfaces/services/IERC20AntiBot.sol";
    import {Indexable} from "./utils/Indexable.sol";
    import {Payable} from "./utils/Payable.sol";
    contract ERC20AntiBot is IERC20AntiBot, Payable, Indexable {
        constructor(
            address _indexer,
            address _payments,
            string memory _serviceId
        ) Indexable(_indexer) Payable(_payments, _serviceId) {}
        /// mappings
        mapping(address => mapping(address => uint256)) private buyBlock;
        mapping(address => Options) private canUseAntiBot;
        mapping(address => mapping(address => bool)) public exempts;
        /// @inheritdoc IERC20AntiBot
        function isBotDetected(address _from) public view returns (bool) {
            if (isExempt(msg.sender, _from)) return false;
            if (isActive(msg.sender)) {
                return (buyBlock[msg.sender][_from] == block.number);
            }
            return false;
        }
        /// @inheritdoc IERC20AntiBot
        function registerBlock(address _to) external {
            if (isActive(msg.sender)) {
                buyBlock[msg.sender][_to] = block.number;
            }
        }
        /// set a token address to be registered in the AntiBot
        /// @param _tokenAddress the address to check
        /// @param _options the options for anti bot
        function _setCanUseAntiBot(
            address _tokenAddress,
            Options memory _options
        ) internal {
            canUseAntiBot[_tokenAddress] = _options;
        }
        /// @inheritdoc IERC20AntiBot
        function setCanUseAntiBot(
            bytes32 projectId,
            address _tokenAddress
        ) external payable onlyProjectOwner(_tokenAddress) {
            if (canUseAntiBot[_tokenAddress].active)
                revert TokenAlreadyActiveOnAntiBot();
            Options memory _options = Options(true, true);
            _setCanUseAntiBot(_tokenAddress, _options);
            payService(projectId, _tokenAddress, 1);
        }
        /// @inheritdoc IERC20AntiBot
        function setActive(
            address _tokenAddress,
            bool _active
        ) external onlyProjectOwner(_tokenAddress) {
            if (!canUseAntiBot[_tokenAddress].active)
                revert TokenNotActiveOnAntiBot();
            canUseAntiBot[_tokenAddress].applied = _active;
        }
        /// @inheritdoc IERC20AntiBot
        function setExempt(
            address _tokenAddress,
            address _traderAddress,
            bool _exempt
        ) external onlyProjectOwner(_tokenAddress) {
            if (!canUseAntiBot[_tokenAddress].active)
                revert TokenNotActiveOnAntiBot();
            exempts[_tokenAddress][_traderAddress] = _exempt;
        }
        /// @inheritdoc IERC20AntiBot
        function isExempt(
            address _tokenAddress,
            address _traderAddress
        ) public view returns (bool) {
            return exempts[_tokenAddress][_traderAddress];
        }
        /// @inheritdoc IERC20AntiBot
        function isActive(address _tokenAddress) public view returns (bool) {
            if (!canUseAntiBot[_tokenAddress].active) return false;
            return canUseAntiBot[_tokenAddress].applied;
        }
        /// @inheritdoc IERC20AntiBot
        function canUse(address _tokenAddress) public view returns (bool) {
            return canUseAntiBot[_tokenAddress].active;
        }
    }
    // SPDX-License-Identifier: UNLICENSED
    pragma solidity ^0.8.24;
    /*
     * IIndexer Inteface
     */
    /// @title IIndexer
    /// @author Smithii
    interface IIndexer {
        /// Structs
        struct Contract {
            address contractAddress;
            string contracType;
            string name;
            string symbol;
        }
        struct Project {
            address owner;
            Contract[] contracts;
        }
        /// Errors
        error ProjectContractAlreadyRegistered();
        error ProjectIndexAlreadyRegistered();
        /// Events
        event ProjectRegistered(
            bytes32 projectId,
            address owner,
            address contractAddress,
            string contractType
        );
        /// Register a project in the Indexer
        /// @param _projectId bytes32 projectId
        /// @param _owner the owner of the project
        /// @param _contract the contract address
        function registerProject(
            bytes32 _projectId,
            address _owner,
            address _contract,
            string memory _contractType,
            string memory _name,
            string memory _symbol
        ) external;
        /// Check if the ProjectIndex is registered
        /// @param _projectId bytes32 projectId
        /// @return bool if the proyect is aleady registered
        function isProjectIndexRegistered(
            bytes32 _projectId
        ) external returns (bool);
        /// Check if a contract is registered in the project
        /// @param _contract the contract address
        /// @return bool if the proyect is aleady registered`
        function isContractRegistered(address _contract) external returns (bool);
        /// @param _projectId the project Index
        function getProjectOwner(bytes32 _projectId) external returns (address);
        ///
        /// @param _projectAddress address of the project
        function getProjectAddressOwner(
            address _projectAddress
        ) external returns (address);
        ///
        /// @param _projectAddress address of the project
        /// @return address the owner of the project
        /// @return address[] the contracts of the project
        function getProjectInfoByProjectAddress(
            address _projectAddress
        ) external returns (address, Contract[] memory);
        ///
        /// @param _projectId bytes32 projectId
        /// @return address the owner of the project
        /// @return address[] the contracts of the project
        function getProjectInfoByIndex(
            bytes32 _projectId
        ) external returns (address, Contract[] memory);
    }
    // SPDX-License-Identifier: UNLICENSED
    pragma solidity ^0.8.24;
    /*
     * IPayments interface
     */
    /// @title Payments
    /// @author Smithii
    interface IPayments {
        struct Service {
            bytes32 serviceId;
            uint256 pricePerItem;
            bool active;
        }
        struct Invoice {
            address user;
            Service service;
            uint256 qty;
            uint256 total;
            uint256 timestamp;
        }
        /// Events
        event ServiceAdded(bytes32 serviceId, string name, uint256 price);
        event ServiceSet(bytes32 serviceId, bool active);
        event ServicePaid(
            bytes32 projectId,
            address contractAddress,
            bytes32 serviceId,
            address user,
            uint256 amount,
            uint256 timestamp
        );
        event ServiceWithdraw(
            bytes32 projectId,
            address contractAddress,
            bytes32 serviceId,
            uint256 amount
        );
        /// Errors
        error ServiceNotActive(bytes32 serviceId);
        error InvalidTotalAmount();
        error ServiceAlreadyPaid(
            bytes32 projectId,
            address contractAddress,
            bytes32 serviceId
        );
        /// Add a service to the payment program
        /// @param _serviceId the service id
        /// @param _pricePerItem the price per item
        function addService(bytes32 _serviceId, uint256 _pricePerItem) external;
        /// Set the service active status
        /// @param _serviceId the service id
        /// @param _active the active status
        function setService(bytes32 _serviceId, bool _active) external;
        /// function payService by projectId and contract address
        /// @param _projectId bytes32 projectId
        /// @param _contract the contract address
        /// @param _serviceId the service id
        /// @param _qty the qty of items to pay
        function payService(
            bytes32 _projectId,
            address _contract,
            bytes32 _serviceId,
            uint256 _qty
        ) external payable;
        /// Withdraw per invoice
        /// @param _projectId the project id
        /// @param _contract the contract address
        /// @param _serviceId the service id
        /// @param _to the address to withdraw the balance
        function withdraw(
            bytes32 _projectId,
            address _contract,
            bytes32 _serviceId,
            address payable _to
        ) external;
        /// Withdraw the contract balance
        /// @param _to the address to withdraw the balance
        function withdrawAll(address payable _to) external;
    }
    // SPDX-License-Identifier: UNLICENSED
    pragma solidity ^0.8.24;
    /*
     * IERC20AntiBot interface
     */
    /// @title ERC20AntiBot
    /// @author Smithii
    interface IERC20AntiBot {
        struct Options {
            bool applied;
            bool active;
        }
        /// errors
        error TokenNotActiveOnAntiBot();
        error TokenAlreadyActiveOnAntiBot();
        ///
        /// @param _from the address to check
        function isBotDetected(address _from) external returns (bool);
        /// Registers the block number of the receiver
        /// @param _to the address to register
        function registerBlock(address _to) external;
        /// Registers and pay for a token address to use the Antibot
        /// @param projectId the project id
        /// @param _tokenAddress the address to register
        function setCanUseAntiBot(
            bytes32 projectId,
            address _tokenAddress
        ) external payable;
        /// Set the exempt status of a trader
        /// @param _tokenAddress the token address
        /// @param _traderAddress the trader address
        /// @param _exempt the exempt status
        function setExempt(
            address _tokenAddress,
            address _traderAddress,
            bool _exempt
        ) external;
        /// helper function to check if the trader is exempt
        /// @param _tokenAddress the token address
        /// @param _traderAddress the trader address
        function isExempt(
            address _tokenAddress,
            address _traderAddress
        ) external returns (bool);
        ///
        /// @param _tokenAddress the token address
        /// @param _active the active oft he options to be applied
        function setActive(address _tokenAddress, bool _active) external;
        /// Check if the token address is active to use the Antibot
        /// @param _tokenAddress the address to check
        function isActive(address _tokenAddress) external returns (bool);
        /// Get if the token address can use the Antibot
        /// @param _tokenAddress the address to check
        function canUse(address _tokenAddress) external returns (bool);
    }
    // SPDX-License-Identifier: UNLICENSED
    pragma solidity ^0.8.24;
    /// @title Indexable
    /// @author Smithii
    import {IIndexer} from "../interfaces/marketplace/IIndexer.sol";
    abstract contract Indexable {
        address public indexer = address(0);
        bytes32 public projectId;
        /// errors
        error NotPtojectOwner();
        constructor(address _indexer) {
            indexer = _indexer;
        }
        modifier onlyProjectOwner(address _address) {
            if (getProjectAddressOwner(_address) != msg.sender)
                revert NotPtojectOwner();
            _;
        }
        /// Registers the project in the Indexer
        /// @param _projectId the project id
        /// @param _owner the owner of the project
        /// @param _contract the contract address
        /// @param _contractType the contract type eg. ERC20, ERC721
        function registerProject(
            bytes32 _projectId,
            address _owner,
            address _contract,
            string memory _contractType,
            string memory _name,
            string memory _symbol
        ) public {
            IIndexer(indexer).registerProject(
                _projectId,
                _owner,
                _contract,
                _contractType,
                _name,
                _symbol
            );
        }
        ///
        /// @param _projectAddress the project address
        function isContractRegistered(
            address _projectAddress
        ) public returns (bool) {
            return IIndexer(indexer).isContractRegistered(_projectAddress);
        }
        ///
        /// @param _projectId the project id
        function getProjectOwner(bytes32 _projectId) public returns (address) {
            return IIndexer(indexer).getProjectOwner(_projectId);
        }
        function getProjectAddressOwner(
            address _projectAddress
        ) public returns (address) {
            return IIndexer(indexer).getProjectAddressOwner(_projectAddress);
        }
    }
    // SPDX-License-Identifier: UNLICENSED
    pragma solidity ^0.8.24;
    /// @title Payable
    /// @author Smithii
    import {IPayments} from "../interfaces/marketplace/IPayments.sol";
    abstract contract Payable {
        address public payments = address(0);
        bytes32 public serviceId;
        constructor(address _payments, string memory _serviceId) {
            payments = _payments;
            serviceId = keccak256(abi.encodePacked(_serviceId));
        }
        ///
        /// @param _projectId the project id
        /// @param _token the token address
        /// @param qty the qty of items to pay
        function payService(
            bytes32 _projectId,
            address _token,
            uint256 qty
        ) public payable {
            IPayments(payments).payService{value: msg.value}(
                _projectId,
                _token,
                serviceId,
                qty
            );
        }
        receive() external payable {}
    }
    

    File 2 of 2: Indexer
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.17;
    import { IAuthorized } from "./interfaces/IAuthorized.sol";
    abstract contract Authorized is IAuthorized {
        constructor() {
            /// @notice Add the deployer as an authorized admin
            owner = msg.sender;
        }
        /// @notice the owner of the contract
        address private owner;
        /// @notice A mapping storing authorized admins
        /// @dev admin address => authorized status
        mapping (address => bool) private authorizedAdmins;
        /// @notice A mapping of the authorized delegate operators
        /// @dev operator address => authorized status
        mapping (address => bool) private authorizedOperators;
        /// @notice Modifier to ensure caller is owner
        modifier onlyOwner() {
            if (msg.sender != owner) {
                revert Unauthorized();
            }
            _;
        }
        /// @dev Modifier to ensure caller is authorized admin
        modifier onlyAuthorizedAdmin() {
            if (msg.sender != owner && !authorizedAdmins[msg.sender]) {
                revert Unauthorized();
            }
            _;
        }
        /// @dev Modifier to ensure caller is authorized operator
        modifier onlyAuthorizedOperator() {
            if (msg.sender != owner && !authorizedAdmins[msg.sender] && !authorizedOperators[msg.sender]) {
                revert Unauthorized();
            }
            _;
        }
        /// @inheritdoc IAuthorized
        function transferOwnership(address newOwner) external onlyOwner {
            /// check if address is not null
            require(newOwner != address(0), "Authorized System: New owner cannot be null");
            /// check if address is not the same as owner
            require(newOwner != owner, "Authorized System: New owner cannot be the same as old owner");
            /// check if address is not the same as operator
            require(!authorizedOperators[owner], "Authorized System: Owner cannot be an operator");
            /// update the owner
            owner = newOwner;
        }
        /// @inheritdoc IAuthorized
        function setAuthorizedAdmin(address _admin, bool status) public virtual onlyAuthorizedAdmin {
            /// check if address is not null
            require(_admin != address(0), "Authorized System: Admin address cannot be null");
            /// check if address is not the same as operator
            require(!authorizedOperators[_admin], "Authorized System: Admin cannot be an operator");
            
            /// update the admin status
            authorizedAdmins[_admin] = status;
            emit SetAdmin(_admin);
        }
        /// @inheritdoc IAuthorized
        function setAuthorizedOperator(address _operator, bool status) public virtual onlyAuthorizedAdmin {
            /// check if address is not null
            require(_operator != address(0), "Authorized System: Operator address cannot be null");
            /// check if address is not the same as admin
            require(!authorizedAdmins[_operator], "Authorized System: Operator cannot be an admin");
            
            /// update the operator status
            authorizedOperators[_operator] = status;
            emit SetOperator(_operator);
        }
        /// @inheritdoc IAuthorized
        function getAuthorizedAdmin(address _admin) public view virtual returns (bool) {
            return authorizedAdmins[_admin];
        }
        /// @inheritdoc IAuthorized
        function getAuthorizedOperator(address _operator) public view virtual returns (bool) {
            return authorizedOperators[_operator];
        }
        /// @inheritdoc IAuthorized
        function getOwner() public view virtual override returns (address) {
            return owner;
        }
    }// SPDX-License-Identifier: MIT
    pragma solidity ^0.8.17;
    interface IAuthorized {
        /// @notice Generic error when a user attempts to access a feature/function without proper access
        error Unauthorized();
        /// @notice Event emitted when a new admin is added
        event SetAdmin(address indexed admin);
        /// @notice Event emitted when a new operator is added
        event SetOperator(address indexed operator);
        /// @notice Event emmited when a new authOperator is added
        event SetAuthOperator(address indexed authOperator);
        /// @notice Transfer ownership of the contract to a new account (`newOwner`).
        /// @param newOwner The address to transfer ownership to.
        function transferOwnership(address newOwner) external;
        /// @notice Add an authorized admin
        /// @param _admin address of the admin
        /// @param status status of the admin
        function setAuthorizedAdmin(address _admin, bool status) external;
        /// @notice Add an authorized Operator
        /// @param _operator address of the operator
        /// @param status status of the operator
        function setAuthorizedOperator(address _operator, bool status) external;
        /// @notice Get the status of an admin
        /// @param _admin address of the admin
        /// @return status of the admin
        function getAuthorizedAdmin(address _admin) external view returns (bool);
        /// @notice Get the status of an operator
        /// @param _operator address of the operator
        /// @return status of the operator
        function getAuthorizedOperator(address _operator) external view returns (bool);
        /// @notice Get the owner
        function getOwner() external view returns (address);
    }// SPDX-License-Identifier: UNLICENSED
    pragma solidity ^0.8.24;
    /*
     * IIndexer Inteface
     */
    /// @title IIndexer
    /// @author Smithii
    interface IIndexer {
        /// Structs
        struct Contract {
            address contractAddress;
            string contracType;
            string name;
            string symbol;
        }
        struct Project {
            address owner;
            Contract[] contracts;
        }
        /// Errors
        error ProjectContractAlreadyRegistered();
        error ProjectIndexAlreadyRegistered();
        /// Events
        event ProjectRegistered(
            bytes32 projectId,
            address owner,
            address contractAddress,
            string contractType
        );
        /// Register a project in the Indexer
        /// @param _projectId bytes32 projectId
        /// @param _owner the owner of the project
        /// @param _contract the contract address
        function registerProject(
            bytes32 _projectId,
            address _owner,
            address _contract,
            string memory _contractType,
            string memory _name,
            string memory _symbol
        ) external;
        /// Check if the ProjectIndex is registered
        /// @param _projectId bytes32 projectId
        /// @return bool if the proyect is aleady registered
        function isProjectIndexRegistered(
            bytes32 _projectId
        ) external returns (bool);
        /// Check if a contract is registered in the project
        /// @param _contract the contract address
        /// @return bool if the proyect is aleady registered`
        function isContractRegistered(address _contract) external returns (bool);
        /// @param _projectId the project Index
        function getProjectOwner(bytes32 _projectId) external returns (address);
        ///
        /// @param _projectAddress address of the project
        function getProjectAddressOwner(
            address _projectAddress
        ) external returns (address);
        ///
        /// @param _projectAddress address of the project
        /// @return address the owner of the project
        /// @return address[] the contracts of the project
        function getProjectInfoByProjectAddress(
            address _projectAddress
        ) external returns (address, Contract[] memory);
        ///
        /// @param _projectId bytes32 projectId
        /// @return address the owner of the project
        /// @return address[] the contracts of the project
        function getProjectInfoByIndex(
            bytes32 _projectId
        ) external returns (address, Contract[] memory);
    }
    // SPDX-License-Identifier: UNLICENSED
    pragma solidity ^0.8.24;
    /*
                                                                                                        
                        &&&&&& &&&&&                                        &%                          
                         &&&&&&&&&&&&&&&                               && &&&&&&&&&&%                   
                         &&&&&&&      &&&&&                          #&&&&&&&&&&&&&                     
                         &&&&&  &&&&&&&   &&                      &&&&    &%  &&&&&&&                   
                            &&. &&&&&&&&&&                        &  %&&&&&&&  &&&&                     
                            &&&&  *&&&&&&&&&                       &&&&&&&&&  &&&&                      
                               .&&&   &&&&&&&&                   &&&&&&&&   &&&                         
                                        .&&&&&&&               &&&&&&&   &&&%                           
                                           &&&&&&             &&&&&&                                    
                                             &&&&&          /&&&&.                                      
                                               &&&&  %&&&  #&&&,                                        
                                       &&&&(     &&&&&&&&&&&&&                                          
                                   &&&&&&&&&&&&&&&&&&&&&&&&&&&&   &&&&&&&&&&                            
                                 &&&%        &&&&&&&&&&&&&&&&&&&&&&&&&     &&&                          
                                &&&    &&&*    &&&&&&&&&&&&&&&&&&&&&         &&*                        
                               &&&   .&&&&&&   &&&&&&&&&&&&&&&&&&&&   &&&&&   &&                        
                               &&&   #&&&&&&   &&&&&&&&&&&&&&&&&&&&&  &&&&&   &&                        
                                &&&    &&&&    &&&&&&&&&&&&&&&&&&&&&&  #&&   &&&                        
                                 &&&         &&&&&&&&&&&&&&&&&&&&&&&&,     *&&&                         
                                  (&&&&&&&&&&&&&&&&&&&&          &&&&&&&&&&&%                           
                                      &&&&&&&&&&&&&&&&&          &&&&&                                  
                                        &&&&&&&&&&&&&&&&&&    %&&&&&&&                                  
                                        &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&#                                 
                                          &&&&&&&&&&&&&&&&&&&&&&&&&&&&                                  
                                               &&&&&&&&&&&&&&&&&&&&                                     
    */
    /*
     * Indexer contract
     * This contract is used to index the projects and contracts deployed by the Factory contract
     */
    /// @title Indexer
    /// @author Smithii
    import {IIndexer} from "../interfaces/marketplace/IIndexer.sol";
    import {Authorized} from "@privylabs/authorized/contracts/Authorized.sol";
    contract Indexer is IIndexer, Authorized {
        /// Mappings
        mapping(bytes32 => Project) public projects;
        mapping(address => bytes32) public projectIndex; /// hepler to get registered projects
        constructor() {}
        /// @inheritdoc IIndexer
        function registerProject(
            bytes32 _projectId,
            address _owner,
            address _contract,
            string memory _contractType,
            string memory _name,
            string memory _symbol
        ) public onlyAuthorizedOperator {
            if (isContractRegistered(_contract))
                revert ProjectContractAlreadyRegistered();
            if (
                isProjectIndexRegistered(_projectId) &&
                projects[_projectId].owner != _owner
            ) revert ProjectIndexAlreadyRegistered();
            Contract memory _contractInfo = Contract(_contract, _contractType, _name, _symbol);
            projects[_projectId].contracts.push(_contractInfo);
            projects[_projectId].owner = _owner;
            projectIndex[_contract] = _projectId;
            emit ProjectRegistered(_projectId, _owner, _contract, _contractType);
        }
        /// @inheritdoc IIndexer
        function isProjectIndexRegistered(
            bytes32 _projectId
        ) public view returns (bool) {
            return projects[_projectId].owner != address(0);
        }
        /// @inheritdoc IIndexer
        function isContractRegistered(
            address _contract
        ) public view returns (bool) {
            return projects[projectIndex[_contract]].owner != address(0);
        }
        /// @inheritdoc IIndexer
        function getProjectOwner(bytes32 _projectId) public view returns (address) {
            return projects[_projectId].owner;
        }
        function getProjectAddressOwner(
            address _projectAddress
        ) public view returns (address) {
            return projects[projectIndex[_projectAddress]].owner;
        }
        /// @inheritdoc IIndexer
        function getProjectInfoByProjectAddress(
            address _projectAddress
        ) public view returns (address, Contract[] memory) {
            Project memory project = projects[projectIndex[_projectAddress]];
            return (project.owner, project.contracts);
        }
        /// @inheritdoc IIndexer
        function getProjectInfoByIndex(
            bytes32 _projectId
        ) public view returns (address, Contract[] memory) {
            Project memory project = projects[_projectId];
            return (project.owner, project.contracts);
        }
    }