// File: contracts/tests/TestQuadrata.sol



//SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

import "../interfaces/IQuadReader.sol";
import "../interfaces/IQuadPassportStore.sol";

contract TestQuadrata {
   address public admin;
    IQuadReader public reader;

    constructor() {
        admin = msg.sender;
    }
    function setReader(address _reader) public {
        reader = IQuadReader(_reader);
    }

    function checkValues(address _account, bytes32 _attribute, bytes32 _value, uint256 i) public payable {
        uint256 queryFee = reader.queryFee(_account, _attribute);

        require(msg.value >= queryFee, "NOT_ENOUGH_FEE");
        IQuadPassportStore.Attribute[] memory attributes = reader.getAttributes{value: queryFee}(_account, _attribute);
        require(attributes.length > i, "NO_ATTRIBUTE_FOUND");
        require(attributes[i].value == _value, "MISMATCH_VALUE");
    }

    function checkValuesInt(address _account, bytes32 _attribute, uint256 _value, uint256 i) public payable {
        uint256 queryFee = reader.queryFee(_account, _attribute);

        require(msg.value >= queryFee, "NOT_ENOUGH_FEE");
        IQuadPassportStore.Attribute[] memory attributes = reader.getAttributes{value: queryFee}(_account, _attribute);
        require(attributes.length > i, "NO_ATTRIBUTE_FOUND");
        require(uint256(attributes[i].value) == _value, "MISMATCH_VALUE");
    }

    function checkIssuer(address _account, bytes32 _attribute, address _issuer, uint256 i) public payable {
        uint256 queryFee = reader.queryFee(_account, _attribute);

        require(msg.value >= queryFee, "NOT_ENOUGH_FEE");
        IQuadPassportStore.Attribute[] memory attributes = reader.getAttributes{value: queryFee}(_account, _attribute);
        require(attributes.length > i, "NO_ATTRIBUTE_FOUND");
        require(attributes[i].issuer == _issuer, "MISMATCH_ISSUER");
    }

    function checkBeforeEpoch(address _account, bytes32 _attribute, uint256 _epoch, uint256 i) public payable {

        uint256 queryFee = reader.queryFee(_account, _attribute);

        require(msg.value >= queryFee, "NOT_ENOUGH_FEE");
        IQuadPassportStore.Attribute[] memory attributes = reader.getAttributes{value: queryFee}(_account, _attribute);
        require(attributes.length > i, "NO_ATTRIBUTE_FOUND");
        require(attributes[i].epoch <= _epoch, "MISMATCH_EPOCH");
    }

    function checkNumberAttributes(address _account, bytes32 _attribute, uint256 _number) public payable {
        uint256 queryFee = reader.queryFee(_account, _attribute);

        require(msg.value >= queryFee, "NOT_ENOUGH_FEE");
        IQuadPassportStore.Attribute[] memory attributes = reader.getAttributes{value: queryFee}(_account, _attribute);
        require(attributes.length == _number, "INVALID_NUMBER_ATTRIBUTES");
    }
}

// File: contracts/interfaces/IQuadReader.sol



//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.16;

import "../storage/QuadPassportStore.sol";

interface IQuadReader {
    event QueryEvent(address indexed _account, address indexed _caller, bytes32 _attribute);
    event QueryBulkEvent(address indexed _account, address indexed _caller, bytes32[] _attributes);
    event QueryFeeReceipt(address indexed _receiver, uint256 _fee);
    event WithdrawEvent(address indexed _issuer, address indexed _treasury, uint256 _fee);

    function queryFee(
        address _account,
        bytes32 _attribute
    ) external view returns(uint256);

    function queryFeeBulk(
        address _account,
        bytes32[] calldata _attributes
    ) external view returns(uint256);

    function getAttributes(
        address _account, bytes32 _attribute
    ) external payable returns(QuadPassportStore.Attribute[] memory attributes);

    function getAttributesLegacy(
        address _account, bytes32 _attribute
    ) external payable returns(bytes32[] memory values, uint256[] memory epochs, address[] memory issuers);

    function getAttributesBulk(
        address _account, bytes32[] calldata _attributes
    ) external payable returns(QuadPassportStore.Attribute[] memory);

    function getAttributesBulkLegacy(
        address _account, bytes32[] calldata _attributes
    ) external payable returns(bytes32[] memory values, uint256[] memory epochs, address[] memory issuers);

    function balanceOf(address _account, bytes32 _attribute) external view returns(uint256);

    function latestEpoch(address _account, bytes32 _attribute) external view returns(uint256);

    function hasPassportByIssuer(address _account, bytes32 _attribute, address _issuer) external view returns(bool);

    function withdraw(address payable _to, uint256 _amount) external;

    // @dev DEPRECATED - use `queryFee` instead
    function calculatePaymentETH(
        bytes32 _attribute,
        address _account
    ) external view returns(uint256);


    // @dev DEPRECATED - use `getAttributesLegacy` instead
    function getAttributesETH(
        address _account,
        uint256 _tokenId,
        bytes32 _attribute
    ) external payable returns(bytes32[] memory, uint256[] memory, address[] memory);
}

// File: contracts/interfaces/IQuadPassportStore.sol



//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.16;

interface IQuadPassportStore {

    /// @dev Attribute store infomation as it relates to a single attribute
    /// `attrKeys` Array of keys defined by (wallet address/DID + data Type)
    /// `value` Attribute value
    /// `epoch` timestamp when the attribute has been verified by an Issuer
    /// `issuer` address of the issuer issuing the attribute
    struct Attribute {
        bytes32 value;
        uint256 epoch;
        address issuer;
    }

    /// @dev AttributeSetterConfig contains configuration for setting attributes for a Passport holder
    /// @notice This struct is used to abstract setAttributes function parameters
    /// `attrKeys` Array of keys defined by (wallet address/DID + data Type)
    /// `attrValues` Array of attributes values
    /// `attrTypes` Array of attributes types (ex: [keccak256("DID")]) used for validation
    /// `did` did of entity
    /// `tokenId` tokenId of the Passport
    /// `issuedAt` epoch when the passport has been issued by the Issuer
    /// `verifiedAt` epoch when the attribute has been attested by the Issuer
    /// `fee` Fee (in Native token) to pay the Issuer
    struct AttributeSetterConfig {
        bytes32[] attrKeys;
        bytes32[] attrValues;
        bytes32[] attrTypes;
        bytes32 did;
        uint256 tokenId;
        uint256 verifiedAt;
        uint256 issuedAt;
        uint256 fee;
    }
}

// File: contracts/storage/QuadPassportStore.sol



//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.16;

import "../interfaces/IQuadPassportStore.sol";
import "../interfaces/IQuadGovernance.sol";

import "./QuadConstant.sol";

contract QuadPassportStore is IQuadPassportStore, QuadConstant {

    IQuadGovernance public governance;
    address public pendingGovernance;

    // SignatureHash => bool
    mapping(bytes32 => bool) internal _usedSigHashes;

    string public symbol;
    string public name;

    // Key could be:
    // 1) keccak256(userAddress, keccak256(attrType))
    // 2) keccak256(DID, keccak256(attrType))
    mapping(bytes32 => Attribute[]) internal _attributes;

    // Key could be:
    // 1) keccak256(keccak256(userAddress, keccak256(attrType)), issuer)
    // 1) keccak256(keccak256(DID, keccak256(attrType)), issuer)
    mapping(bytes32 => uint256) internal _position;
}

// File: contracts/interfaces/IQuadGovernance.sol



//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.16;

import "../storage/QuadGovernanceStore.sol";

interface IQuadGovernance {
    event AttributePriceUpdatedFixed(bytes32 _attribute, uint256 _oldPrice, uint256 _price);
    event BusinessAttributePriceUpdatedFixed(bytes32 _attribute, uint256 _oldPrice, uint256 _price);
    event EligibleTokenUpdated(uint256 _tokenId, bool _eligibleStatus);
    event EligibleAttributeUpdated(bytes32 _attribute, bool _eligibleStatus);
    event EligibleAttributeByDIDUpdated(bytes32 _attribute, bool _eligibleStatus);
    event IssuerAdded(address indexed _issuer, address indexed _newTreasury);
    event IssuerDeleted(address indexed _issuer);
    event IssuerStatusChanged(address indexed issuer, bool newStatus);
    event IssuerAttributePermission(address indexed issuer, bytes32 _attribute,  bool _permission);
    event PassportAddressUpdated(address indexed _oldAddress, address indexed _address);
    event RevenueSplitIssuerUpdated(uint256 _oldSplit, uint256 _split);
    event TreasuryUpdated(address indexed _oldAddress, address indexed _address);

    function setTreasury(address _treasury) external;

    function setPassportContractAddress(address _passportAddr) external;

    function updateGovernanceInPassport(address _newGovernance) external;

    function setEligibleTokenId(uint256 _tokenId, bool _eligibleStatus, string memory _uri) external;

    function setEligibleAttribute(bytes32 _attribute, bool _eligibleStatus) external;

    function setEligibleAttributeByDID(bytes32 _attribute, bool _eligibleStatus) external;

    function setAttributePriceFixed(bytes32 _attribute, uint256 _price) external;

    function setBusinessAttributePriceFixed(bytes32 _attribute, uint256 _price) external;

    function setRevSplitIssuer(uint256 _split) external;

    function addIssuer(address _issuer, address _treasury) external;

    function deleteIssuer(address _issuer) external;

    function setIssuerStatus(address _issuer, bool _status) external;

    function setIssuerAttributePermission(address _issuer, bytes32 _attribute, bool _permission) external;

    function getEligibleAttributesLength() external view returns(uint256);

    function getMaxEligibleTokenId() external view returns(uint256);

    function eligibleTokenId(uint256) external view returns(bool);

    function issuersTreasury(address) external view returns (address);

    function eligibleAttributes(bytes32) external view returns(bool);

    function eligibleAttributesByDID(bytes32) external view returns(bool);

    function eligibleAttributesArray(uint256) external view returns(bytes32);

    function pricePerAttributeFixed(bytes32) external view returns(uint256);

    function pricePerBusinessAttributeFixed(bytes32) external view returns(uint256);

    function revSplitIssuer() external view returns (uint256);

    function treasury() external view returns (address);

    function getIssuersLength() external view returns (uint256);

    function getIssuers() external view returns (address[] memory);

    function issuers(uint256) external view returns(address);

    function getIssuerStatus(address _issuer) external view returns(bool);

    function getIssuerAttributePermission(address _issuer, bytes32 _attribute) external view returns(bool);
}

// File: contracts/storage/QuadConstant.sol



//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.16;

contract QuadConstant {
    bytes32 public constant ISSUER_ROLE = keccak256("ISSUER_ROLE");
    bytes32 public constant GOVERNANCE_ROLE = keccak256("GOVERNANCE_ROLE");
    bytes32 public constant READER_ROLE = keccak256("READER_ROLE");
    bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");

    bytes32 public constant DIGEST_TO_SIGN = 0x37937bf5ff1ecbf00bbd389ab7ca9a190d7e8c0a084b2893ece7923be1d2ec85;
    bytes32 internal constant ATTRIBUTE_DID = 0x09deac0378109c72d82cccd3c343a90f7020f0f1af78dcd4fc949c6301aa9488;
    bytes32 internal constant ATTRIBUTE_IS_BUSINESS = 0xaf369ce728c816785c72f1ff0222ca9553b2cb93729d6a803be6af0d2369239b;
    bytes32 internal constant ATTRIBUTE_COUNTRY = 0xc4713d2897c0d675d85b414a1974570a575e5032b6f7be9545631a1f922b26ef;
    bytes32 internal constant ATTRIBUTE_AML = 0xaf192d67680c4285e52cd2a94216ce249fb4e0227d267dcc01ea88f1b020a119;

    uint256[47] private __gap;
}

// File: contracts/storage/QuadGovernanceStore.sol



//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.16;

import "../interfaces/IQuadPassport.sol";

import "./QuadConstant.sol";

contract QuadGovernanceStore is QuadConstant {
    // Attributes
    bytes32[] internal _eligibleAttributesArray;
    mapping(bytes32 => bool) internal _eligibleAttributes;
    mapping(bytes32 => bool) internal _eligibleAttributesByDID;

    // TokenId
    mapping(uint256 => bool) internal _eligibleTokenId;

    // Pricing
    mapping(bytes32 => uint256) internal _pricePerBusinessAttributeFixed;
    mapping(bytes32 => uint256) internal _pricePerAttributeFixed;

    // Issuers
    mapping(address => address) internal _issuerTreasury;
    mapping(address => bool) internal _issuerStatus;
    mapping(bytes32 => bool) internal _issuerAttributePermission;
    address[] internal _issuers;

    // Others
    uint256 internal _revSplitIssuer; // 50 means 50%;
    uint256 internal _maxEligibleTokenId;
    IQuadPassport internal _passport;
    address internal _treasury;
}

// File: contracts/interfaces/IQuadPassport.sol



//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.16;

import "./IQuadPassportStore.sol";
import "./IQuadSoulbound.sol";

interface IQuadPassport is IQuadSoulbound {
    event GovernanceUpdated(address indexed _oldGovernance, address indexed _governance);
    event SetPendingGovernance(address indexed _pendingGovernance);
    event SetAttributeReceipt(address indexed _account, address indexed _issuer, uint256 _fee);
    event BurnPassportsIssuer(address indexed _issuer, address indexed _account);
    event WithdrawEvent(address indexed _issuer, address indexed _treasury, uint256 _fee);

    function setAttributes(
        IQuadPassportStore.AttributeSetterConfig memory _config,
        bytes calldata _sigIssuer,
        bytes calldata _sigAccount
    ) external payable;

    function setAttributesIssuer(
        address _account,
        IQuadPassportStore.AttributeSetterConfig memory _config,
        bytes calldata _sigIssuer
    ) external payable;

    function burnPassports() external;

    function burnPassportsIssuer(address _account) external;

    function setGovernance(address _governanceContract) external;

    function acceptGovernance() external;

    function attributes(address _account, bytes32 _attribute) external view returns (IQuadPassportStore.Attribute[] memory);

    function withdraw(address payable _to, uint256 _amount) external;

    function passportPaused() external view returns(bool);

    function setTokenURI(uint256 _tokenId, string memory _uri) external;
}

// File: contracts/interfaces/IQuadSoulbound.sol



//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.16;

interface IQuadSoulbound  {
    /**
     * @dev Emitted when `value` tokens of token type `id` are transferred from `from` to `to` by `operator`.
     */
    event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);

    /**
     * @dev Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI.
     *
     * If an {URI} event was emitted for `id`, the standard
     * https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[guarantees] that `value` will equal the value
     * returned by {IERC1155MetadataURI-uri}.
     */
    event URI(string value, uint256 indexed id);

    function uri(uint256 _tokenId) external view returns (string memory);

    /**
     * @dev ERC1155 balanceOf implementation
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     */
    function balanceOf(address account, uint256 id) external view returns (uint256);

    /**
     * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}.
     *
     * Requirements:
     *
     * - `accounts` and `ids` must have the same length.
     */
    function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids)
        external
        view
        returns (uint256[] memory);
}