MetaTransactionFacet

Description:

Multi-signature wallet contract requiring multiple confirmations for transaction execution.

Blockchain: Ethereum

Source Code: View Code On The Blockchain

Solidity Source Code:

{{
  "language": "Solidity",
  "sources": {
    "@openzeppelin/contracts/access/IAccessControl.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (access/IAccessControl.sol)

pragma solidity ^0.8.20;

/**
 * @dev External interface of AccessControl declared to support ERC-165 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. This account bears the admin role (for the granted role).
     * Expected in cases where the role was granted using the internal {AccessControl-_grantRole}.
     */
    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;
}
"
    },
    "@openzeppelin/contracts/interfaces/IERC1271.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (interfaces/IERC1271.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC-1271 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);
}
"
    },
    "@openzeppelin/contracts/token/ERC721/IERC721.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC721/IERC721.sol)

pragma solidity ^0.8.20;

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

/**
 * @dev Required interface of an ERC-721 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 ERC-721 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 ERC-721
     * 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);
}
"
    },
    "@openzeppelin/contracts/utils/introspection/IERC165.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/IERC165.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC-165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[ERC].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
"
    },
    "contracts/protocol/bases/mixins/Access.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

import { IAccessControl } from "@openzeppelin/contracts/access/IAccessControl.sol";
import { FermionTypes } from "../../domain/Types.sol";
import { FermionStorage } from "../../libs/Storage.sol";
import { PauseErrors, FermionGeneralErrors } from "../../domain/Errors.sol";
import { Context } from "./Context.sol";
import { ReentrancyGuard } from "./ReentrancyGuard.sol";

/**
 * @title Access control
 *
 * @notice Provides access to the protocol
 */
contract Access is Context, ReentrancyGuard {
    struct RoleData {
        mapping(address account => bool) hasRole;
        bytes32 adminRole;
    }

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

    modifier onlyRole(bytes32 _role) {
        address account = _msgSender();

        if (!_getAccessControlStorage()._roles[_role].hasRole[account])
            revert IAccessControl.AccessControlUnauthorizedAccount(account, _role);
        _;
    }

    modifier notPaused(FermionTypes.PausableRegion _region) {
        // Region enum value must be used as the exponent in a power of 2
        uint256 powerOfTwo = 1 << uint256(_region);
        if ((FermionStorage.protocolStatus().paused & powerOfTwo) == powerOfTwo)
            revert PauseErrors.RegionPaused(_region);
        _;
    }

    /** Checks if the caller is the F-NFT contract owning the token.
     *
     * Reverts if:
     * - The caller is not the F-NFT contract owning the token
     *
     * @param _offerId - offer ID associated with the vault
     * @param pl - the number of tokens to add to the vault
     */
    function verifyFermionFNFTCaller(uint256 _offerId, FermionStorage.ProtocolLookups storage pl) internal view {
        if (msg.sender != pl.offerLookups[_offerId].fermionFNFTAddress)
            revert FermionGeneralErrors.AccessDenied(msg.sender); // not using _msgSender() since the FNFT will never use meta transactions
    }
}
"
    },
    "contracts/protocol/bases/mixins/Context.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

import { ContextLib } from "../../libs/ContextLib.sol";

/**
 * @title Execution context
 *
 * @notice Provides the message sender
 */
contract Context {
    /**
     * @notice Returns the message sender address.
     *
     * @dev Could be msg.sender or the message sender address from storage (in case of meta transaction).
     *
     * @return the message sender address
     */
    function _msgSender() internal view virtual returns (address) {
        return ContextLib._msgSender();
    }
}
"
    },
    "contracts/protocol/bases/mixins/ReentrancyGuard.sol": {
      "content": "// SPDX-License-Identifier: CC0-1.0
pragma solidity 0.8.24;

/**
 * @title ReentrancyGuard
 *
 * @notice Prevent reeentrancy on a facet function level
 */
contract ReentrancyGuard {
    uint256 internal constant GUARD_SLOT = 0;
    uint256 internal constant GUARD_LOCKED = 1;
    uint256 internal constant GUARD_UNLOCKED = 0;
    uint256 internal constant REVERT_DATA_OFFSET = 0x1c;
    uint256 internal constant REVERT_DATA_SIZE = 0x04;
    bytes4 internal constant REENTRANCY_ERROR_SELECTOR = 0xb5dfd9e5;

    error Reentered();

    modifier nonReentrant() {
        // NB: it's more optimal to compare msg.sender to address(this) twice than storing it in a variable (e.g. _isSelf)
        // - it's cheaper
        // - it does not add a variable to the stack and cause stack too deep errors
        if (msg.sender != address(this)) {
            assembly {
                if tload(GUARD_SLOT) {
                    mstore(0, REENTRANCY_ERROR_SELECTOR)
                    revert(REVERT_DATA_OFFSET, REVERT_DATA_SIZE)
                }
                tstore(GUARD_SLOT, GUARD_LOCKED)
            }
        }
        _;
        // Unlocks the guard, making the pattern composable.
        // After the function exits, it can be called again, even in the same transaction.
        if (msg.sender != address(this)) {
            assembly {
                tstore(GUARD_SLOT, GUARD_UNLOCKED)
            }
        }
    }
}
"
    },
    "contracts/protocol/domain/Constants.sol": {
      "content": "// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.24;
import { FermionTypes } from "./Types.sol";

// Access Control Roles
bytes32 constant ADMIN = keccak256("ADMIN"); // Role Admin
bytes32 constant PAUSER = keccak256("PAUSER"); // Role for pausing the protocol
bytes32 constant UPGRADER = keccak256("UPGRADER"); // Role for performing contract and config upgrades
bytes32 constant FEE_COLLECTOR = keccak256("FEE_COLLECTOR"); // Role for collecting fees from the protocol

uint256 constant BYTE_SIZE = 8;
uint256 constant SLOT_SIZE = 32;
uint256 constant BOSON_DR_ID_OFFSET = 2; // Boson DR id is 2 higher than the seller id
uint256 constant HUNDRED_PERCENT = 100_00;
uint256 constant AUCTION_END_BUFFER = 15 minutes;
uint256 constant MINIMAL_BID_INCREMENT = 10_00; // 10%

// Fractionalization
uint256 constant MIN_FRACTIONS = 1e18;
uint256 constant MAX_FRACTIONS = 1 << 127;

// buyout exit price governance update
uint256 constant MIN_QUORUM_PERCENT = 20_00; // 20% is the minumum quorum percent for DAO exit price update
uint256 constant MIN_GOV_VOTE_DURATION = 1 days;
uint256 constant MAX_GOV_VOTE_DURATION = 7 days;
uint256 constant DEFAULT_GOV_VOTE_DURATION = 3 days;

// Default parameters
uint256 constant TOP_BID_LOCK_TIME = 3 days;
uint256 constant AUCTION_DURATION = 5 days;
uint256 constant UNLOCK_THRESHOLD = 50_00; // 50%

// Forceful fractionalisation
uint256 constant DEFAULT_FRACTION_AMOUNT = 1e5 * MIN_FRACTIONS;
uint256 constant PARTIAL_THRESHOLD_MULTIPLIER = 12;
uint256 constant LIQUIDATION_THRESHOLD_MULTIPLIER = 2;
uint256 constant PARTIAL_AUCTION_DURATION_DIVISOR = 4;

FermionTypes.EntityRole constant ANY_ENTITY_ROLE = FermionTypes.EntityRole(0);
"
    },
    "contracts/protocol/domain/Errors.sol": {
      "content": "// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.24;

import { FermionTypes } from "./Types.sol";
import "seaport-types/src/lib/ConsiderationStructs.sol" as SeaportTypes;

interface FermionGeneralErrors {
    // General errors
    error InvalidAddress();
    error ArrayLengthMismatch(uint256 expectedLength, uint256 actualLength);
    error AccessDenied(address caller);
    error InvalidPercentage(uint256 percentage);
    error ZeroNotAllowed();
    error UnexpectedDataReturned(bytes data);
    // Array elements that are not in ascending order (i.e arr[i-1] > arr[i])
    error NonAscendingOrder();
    error InvalidTokenId(address fnftAddress, uint256 tokenId);
    error InvalidPeriod();
}

interface InitializationErrors {
    // Initialization errors
    error DirectInitializationNotAllowed();
    error VersionMustBeSet();
    error AddressesAndCalldataLengthMismatch(uint256 addressesLength, uint256 calldataLength);
}

interface EntityErrors {
    // Entity errors
    error EntityAlreadyExists();
    error NoSuchEntity(uint256 entityId);
    error NotRoleManager(address manager, uint256 entityId, FermionTypes.EntityRole role);
    error NotEntityWideRole(address account, uint256 entityId, FermionTypes.AccountRole role);
    error NotAdmin(uint256 entityId, address admin);
    error AlreadyAdmin(uint256 entityId, address admin);
    error EntityHasNoRole(uint256 entityId, FermionTypes.EntityRole role);
    error AccountHasNoRole(
        uint256 entityId,
        address account,
        FermionTypes.EntityRole entityRole,
        FermionTypes.AccountRole accountRole
    );
    error ChangeNotAllowed();
    error NotSellersFacilitator(uint256 sellerId, uint256 facilitatorId);
    error AssociatedEntityAlreadyExists(
        FermionTypes.AssociatedRole associatedRole,
        uint256 sellerId,
        uint256 associatedEntityId
    );
    error NoEntitiesModified(FermionTypes.AssociatedRole associatedRole, uint256 sellerId);
    error AccountAlreadyExists(address account);
    error NewAccountSameAsOld();
}

interface OfferErrors {
    // Offer errors
    error InvalidQuantity(uint256 quantity);
    error NoSuchOffer(uint256 offerId);
    error InvalidOpenSeaOrder();
    error NoPhygitalOffer(uint256 offerId);
    error InvalidRoyaltyInfo();
    error InvalidRoyaltyRecipient(address recipient);
    error InvalidRoyaltyPercentage(uint256 percentage);
    error OfferWithoutRoyalties(uint256 offerId);
    error InvalidCustomItemPrice();
}

interface VerificationErrors {
    // Verification errors
    error VerificationTimeoutNotPassed(uint256 verificationTimeout, uint256 currentTime);
    error VerificationTimeoutTooLong(uint256 verificationTimeout, uint256 maxVerificationTimeout);
    error EmptyMetadata();
    error DigestMismatch(bytes32 expected, bytes32 actual);
    error AlreadyVerified(FermionTypes.VerificationStatus status);
    error InvalidTokenState(uint256 tokenId, FermionTypes.TokenState tokenState);
    error PhygitalsAlreadyVerified(uint256 tokenId);
    error PhygitalsDigestMismatch(uint256 tokenId, bytes32 expectedDigest, bytes32 actualDigest);
    error PhygitalsVerificationMissing(uint256 tokenId);
    error InexistentVerificationStatus();
    error InvalidVerificationStatus();
}

interface CustodyErrors {
    // Custody errors
    error NotTokenBuyer(uint256 tokenId, address owner, address caller);
    error InvalidTaxAmount();
    error InvalidCheckoutRequestStatus(
        uint256 tokenId,
        FermionTypes.CheckoutRequestStatus expectedStatus,
        FermionTypes.CheckoutRequestStatus actualStatus
    );
    error InsufficientVaultBalance(uint256 tokenId, uint256 required, uint256 available);
    error UpdateRequestExpired(uint256 tokenId);
    error UpdateRequestTooRecent(uint256 tokenId, uint256 waitTime);
    error NoTokensInCustody(uint256 offerId);
    error InvalidCustodianFeePeriod();
    error TaxAmountExceedsMaximum(uint256 tokenId, uint256 taxAmount, uint256 maxTaxAmount);
}

interface AuctionErrors {
    error InvalidBid(uint256 tokenId, uint256 minimalBid, uint256 bid);
    error AuctionEnded(uint256 tokenId, uint256 endedAt);
    error AuctionNotStarted(uint256 tokenId);
    error AuctionOngoing(uint256 tokenId, uint256 validUntil);
    error AuctionFinalized(uint256 tokenId);
    error NoFractionsAvailable(uint256 tokenId);
    error NoBids(uint256 tokenId);
    error BidBelowExitPrice(uint256 tokenId, uint256 bid, uint256 exitPrice);
}

interface CustodianVaultErrors is AuctionErrors {
    // Custodian vault
    error InactiveVault(uint256 tokenId);
    error PeriodNotOver(uint256 tokenId, uint256 periodEnd);
    error InvalidPartialAuctionThreshold();
    error InsufficientBalanceToFractionalise(uint256 tokenId, uint256 minimalDeposit);
}

interface FundsErrors {
    // Funds errors
    error WrongValueReceived(uint256 expected, uint256 actual);
    error NativeNotAllowed();
    error PriceTooLow(uint256 price, uint256 minimumPrice);
    error ZeroDepositNotAllowed();
    error NothingToWithdraw();
    error TokenTransferFailed(address to, uint256 amount, bytes errorMessage);
    error InsufficientAvailableFunds(uint256 availableFunds, uint256 requestedFunds);
    error ERC721CheckFailed(address tokenAddress, bool erc721expected);
    error ERC721TokenNotTransferred(address tokenAddress, uint256 tokenId);
    error PhygitalsNotFound(uint256 tokenId, FermionTypes.Phygital phygital);
    error NoNativeFundsToClaim();
}

interface PauseErrors {
    // Pause handler
    error NotPaused();
    error RegionPaused(FermionTypes.PausableRegion region);
}

interface MetaTransactionErrors {
    // Meta transaction errors
    error NonceUsedAlready();
    error FunctionNotAllowlisted();
    error InvalidFunctionName();
    error FunctionCallFailed();
}

interface SignatureErrors {
    error InvalidSignature(); // Somethihing is wrong with the signature
    error SignatureValidationFailed(); // Signature might be correct, but the validation failed
    error InvalidSigner(address expected, address actual);
}

interface FractionalisationErrors is AuctionErrors {
    // Fractionalisation errors
    error InvalidLength();
    error InvalidFractionsAmount(uint256 amount, uint256 min, uint256 max);
    error InvalidExitPrice(uint256 amount);
    error AlreadyFractionalized(uint256 tokenId);
    error PriceOracleNotWhitelisted(address oracleAddress);

    error NotMaxBidder(uint256 tokenId, address caller, address winner);
    error AlreadyRedeemed(uint256 tokenId);
    error NoFractions();
    error InvalidValue(uint256 expected, uint256 actual);
    error BidRemovalNotAllowed(uint256 tokenId);
    error MaxBidderCannotVote(uint256 tokenId);
    error NotEnoughLockedVotes(uint256 tokenId, uint256 lockedVotes, uint256 requestedVotes);
    error InitialFractionalisationOnly();
    error MissingFractionalisation();
    error InvalidAmount();
    error TokenNotFractionalised(uint256 tokenId);
    error InvalidAuctionIndex(uint256 auctionIndex, uint256 numberOfAuctions); // auctionIndex should be less than numberOfAuctions
    error AuctionReserved(uint256 tokenId);
    error ProposalNotActive(uint256 proposalId);
    error AlreadyVoted();
    error NoVotingPower(address voter);
    error OnlyFractionOwner();
    error InvalidVoteDuration(uint256 voteDuration);
    error AlreadyVotedInProposal(uint256 proposalId);
    error InvalidProposalId();
    error ConflictingVote();
    error OracleInternalError();
    error AlreadyMigrated(address owner);
}

interface PriceOracleRegistryErrors {
    error InvalidOracleAddress();
    error InvalidIdentifier();
    error OracleAlreadyApproved();
    error OracleNotApproved();
    error OracleValidationFailed();
    error OracleReturnedInvalidPrice();
}

interface WrapperErrors {
    error ZeroPriceNotAllowed();
    error InvalidOrder(uint256 tokenId, SeaportTypes.OrderComponents order);
    error InvalidOwner(uint256 tokenId, address expected, address actual);
    error InvalidUnwrap();
    error InvalidOpenSeaFee(uint256 actual, uint256 expected);
    error UnsuccessfulExternalCall();
}

interface SafeERC20Errors {
    error SafeERC20FailedOperation(address token);
}
interface FermionErrors is
    FermionGeneralErrors,
    InitializationErrors,
    EntityErrors,
    OfferErrors,
    VerificationErrors,
    CustodyErrors,
    CustodianVaultErrors,
    FundsErrors,
    PauseErrors,
    MetaTransactionErrors,
    FractionalisationErrors,
    SignatureErrors,
    PriceOracleRegistryErrors,
    WrapperErrors,
    SafeERC20Errors
{}
"
    },
    "contracts/protocol/domain/Types.sol": {
      "content": "// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.24;

/**
 * @title FermionTypes
 *
 * @notice Enums and structs used by the Fermion Protocol contract ecosystem.
 */

contract FermionTypes {
    enum EntityRole {
        Seller,
        Buyer,
        Verifier,
        Custodian,
        RoyaltyRecipient
    }

    // Make at most 8 roles so they can be compacted into a byte
    enum AccountRole {
        Manager,
        Assistant,
        Treasury
    }

    enum AssociatedRole {
        Facilitator,
        RoyaltyRecipient
    }

    enum VerificationStatus {
        Verified,
        Rejected,
        Pending
    }

    enum CheckoutRequestStatus {
        None,
        CheckedIn,
        CheckOutRequested,
        CheckOutRequestCleared,
        CheckedOut
    }

    enum PausableRegion {
        Config,
        MetaTransaction,
        Funds,
        Entity,
        Offer,
        Verification,
        Custody,
        CustodyVault
    }

    enum AuctionState {
        NotStarted,
        Ongoing,
        Reserved,
        Finalized,
        Redeemed
    }

    enum TokenState {
        Inexistent,
        Wrapped,
        Unwrapping,
        Unverified,
        Verified,
        CheckedIn,
        CheckedOut,
        Burned
    }

    enum PriceUpdateProposalState {
        NotInit, // Explicitly represents an uninitialized state
        Active,
        Executed,
        Failed
    }

    enum WrapType {
        SELF_SALE,
        OS_AUCTION,
        OS_FIXED_PRICE
    }

    struct EntityData {
        address admin;
        uint256 roles;
        string metadataURI;
    }

    struct MetaTransaction {
        uint256 nonce;
        address from;
        address contractAddress;
        string functionName;
        bytes functionSignature;
    }

    struct Metadata {
        string URI;
        string hash;
    }

    struct Offer {
        uint256 sellerId;
        uint256 sellerDeposit;
        uint256 verifierId;
        uint256 verifierFee;
        uint256 custodianId;
        CustodianFee custodianFee;
        uint256 facilitatorId;
        uint256 facilitatorFeePercent;
        address exchangeToken;
        bool withPhygital;
        Metadata metadata;
        RoyaltyInfo royaltyInfo;
    }

    struct CustodianFee {
        uint256 amount;
        uint256 period;
    }

    struct CheckoutRequest {
        CheckoutRequestStatus status;
        address buyer;
        uint256 taxAmount;
    }

    struct CustodianUpdateRequest {
        uint256 newCustodianId;
        CustodianFee custodianFee;
        CustodianVaultParameters custodianVaultParameters;
        uint256 requestTimestamp;
    }

    struct CustodianVaultParameters {
        uint256 partialAuctionThreshold;
        uint256 partialAuctionDuration;
        uint256 liquidationThreshold;
        uint256 newFractionsPerAuction;
    }

    struct FractionAuction {
        uint256 endTime;
        uint256 availableFractions;
        uint256 maxBid;
        uint256 bidderId;
    }

    // Fermion F-NFT, buyout auction
    struct AuctionDetails {
        uint256 timer;
        uint256 maxBid;
        address maxBidder;
        uint256 totalFractions;
        uint256 lockedFractions;
        uint256 lockedBidAmount;
        AuctionState state;
    }

    struct Votes {
        uint256 total;
        mapping(address => uint256) individual;
    }

    struct BuyoutAuctionStorage {
        uint256 nftCount; // number of fractionalised NFTs
        address exchangeToken;
        BuyoutAuctionParameters auctionParameters;
        uint256 pendingRedeemableSupply; // for tokens that auction started but not finalized yet
        uint256 unrestricedRedeemableSupply;
        uint256 unrestricedRedeemableAmount;
        uint256 lockedRedeemableSupply;
        mapping(uint256 => TokenAuctionInfo) tokenInfo;
        address priceOracle;
        mapping(address => PriceUpdateVoter) voters;
        PriceUpdateProposal[] priceUpdateProposals;
    }

    struct TokenAuctionInfo {
        bool isFractionalised;
        Auction[] auctions;
        int256[] lockedProceeds; // locked for users that voted to start
    }

    struct BuyoutAuctionParameters {
        uint256 exitPrice;
        uint256 duration; // in seconds; if zero, the default value is used
        uint256 unlockThreshold; // in percents; if zero, the default value is used
        uint256 topBidLockTime; // in seconds; if zero, the default value is used
    }

    /// @custom:storage-location erc7201:fermion.fractions.storage
    struct FermionFractionsStorage {
        // Array of ERC20 clone addresses, index is the epoch
        address[] epochToClone;
        uint256 currentEpoch;
        mapping(address => bool) migrated;
    }

    struct PriceUpdateProposal {
        uint256 newExitPrice;
        uint256 votingDeadline;
        uint256 quorumPercent; // in bps (e.g. 2000 is 20%)
        uint256 yesVotes;
        uint256 noVotes;
        PriceUpdateProposalState state;
    }

    struct PriceUpdateVoter {
        uint256 proposalId; // Tracks the ID of the proposal the voter last voted on
        bool votedYes;
        uint256 voteCount;
    }

    struct Auction {
        AuctionDetails details;
        Votes votes;
    }

    struct SplitProposal {
        uint16 buyer;
        uint16 seller;
        bool matching;
    }

    struct Phygital {
        address contractAddress;
        uint256 tokenId;
    }

    struct TokenMetadata {
        string name;
        string symbol;
    }

    struct RoyaltyInfo {
        address payable[] recipients;
        uint256[] bps;
    }

    struct RoyaltyRecipientInfo {
        address payable wallet;
        uint256 minRoyaltyPercentage;
    }
}
"
    },
    "contracts/protocol/facets/MetaTransaction.sol": {
      "content": "// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.24;

import { ADMIN, SLOT_SIZE } from "../domain/Constants.sol";
import { MetaTransactionErrors } from "../domain/Errors.sol";
import { FermionTypes } from "../domain/Types.sol";
import { FermionStorage } from "../libs/Storage.sol";
import { Access } from "../bases/mixins/Access.sol";
import { IMetaTransactionEvents } from "../interfaces/events/IMetaTransactionEvents.sol";
import { EIP712 } from "../libs/EIP712.sol";
import { IFermionFNFT } from "../interfaces/IFermionFNFT.sol";

/**
 * @title MetaTransactionFacet
 *
 * @notice Handles meta-transaction requests.
 */
contract MetaTransactionFacet is Access, EIP712, MetaTransactionErrors, IMetaTransactionEvents {
    bytes32 private constant META_TRANSACTION_TYPEHASH =
        keccak256(
            bytes(
                "MetaTransaction(uint256 nonce,address from,address contractAddress,string functionName,bytes functionSignature)"
            )
        );

    /**
     * @notice Constructor.
     * Store the immutable values and build the domain separator.
     *
     * @param _fermionProtocolAddress - the address of the Fermion Protocol contract
     */
    constructor(address _fermionProtocolAddress) EIP712(_fermionProtocolAddress) {}

    /**
     * @notice Initializes Facet.
     * This function is callable only once.
     *
     * @param _functionNameHashes - a list of hashed function names (keccak256)
     */
    function init(bytes32[] calldata _functionNameHashes) external {
        setAllowlistedFunctionsInternal(_functionNameHashes, true);
        FermionStorage.metaTransaction().fermionAddress = address(this);
    }

    /**
     * @notice Handles the incoming meta transaction.
     *
     * Reverts if:
     * - Metatransaction region is paused
     * - Nonce is already used by the msg.sender for another transaction
     * - Function is not allowlisted to be called using metatransactions
     * - Function name does not match the bytes4 version of the function signature
     * - Any code executed in the signed transaction reverts
     * - The signature verification fails (see EIP712.verify for details)
     *
     * @param _userAddress - the sender of the transaction
     * @param _functionName - the name of the function to be executed
     * @param _functionSignature - the function signature
     * @param _nonce - the nonce value of the transaction
     * @param _sig - meta transaction signature. 
                     If the user is ordinary EOA, it must be ECDSA signature in the format of concatenated r,s,v values. 
                     If the user is a contract, it must be a valid ERC1271 signature.
                     If the user is a EIP-7702 smart account, it can be either a valid ERC1271 signature or a valid ECDSA signature.
     * @param _offerIdWithEpoch - determines where the call is forwarded to. 0 is for Fermion Protocol,
     * a plain offerId is for FermionFNFT associated with offerId, and {epoch+1}{offerId} is for FermionFractions
     * associated with offerId and epoch.
     */
    function executeMetaTransaction(
        address _userAddress,
        string calldata _functionName,
        bytes calldata _functionSignature,
        uint256 _nonce,
        bytes calldata _sig,
        uint256 _offerIdWithEpoch
    ) external payable notPaused(FermionTypes.PausableRegion.MetaTransaction) nonReentrant returns (bytes memory) {
        address userAddress = _userAddress; // stack too deep workaround.
        validateTx(_functionName, _functionSignature, _nonce, userAddress);

        FermionTypes.MetaTransaction memory metaTx;
        metaTx.nonce = _nonce;
        metaTx.from = userAddress;
        metaTx.contractAddress = getContractAddress(_offerIdWithEpoch);
        metaTx.functionName = _functionName;
        metaTx.functionSignature = _functionSignature;

        verify(userAddress, hashMetaTransaction(metaTx), _sig);

        return executeTx(metaTx.contractAddress, userAddress, _functionName, _functionSignature, _nonce);
    }

    /**
     * @notice Gets the destination contract address from the storage.
     *
     * If the offerIdWithEpoch is 0, returns the address of this contract.
     * If upper 128 bits are 0, returns the address of the FermionFNFT contract.
     * Otherwise, subtracts 1 from upper 128 bits to get the epoch and
     * returns the address of the corresponding ERC20 clone.
     *
     * @param _offerIdWithEpoch - determines where the call is forwarded to. 0 is for Fermion Protocol,
     * a plain offerId is for FermionFNFT associated with offerId, and {epoch+1}{offerId} is for FermionFractions
     * associated with offerId and epoch.
     */
    function getContractAddress(uint256 _offerIdWithEpoch) internal view returns (address) {
        if (_offerIdWithEpoch == 0) return address(this);

        uint256 epoch = _offerIdWithEpoch >> 128;
        uint256 offerId = _offerIdWithEpoch & type(uint128).max;

        address FNFTAddress = FermionStorage.protocolLookups().offerLookups[offerId].fermionFNFTAddress;

        if (epoch == 0) return FNFTAddress;

        return IFermionFNFT(FNFTAddress).getERC20FractionsClone(epoch - 1);
    }

    /**
     * @notice Checks nonce and returns true if used already for a specific address.
     *
     * @param _associatedAddress the address for which the nonce should be checked
     * @param _nonce - the nonce that we want to check.
     * @return true if nonce has already been used
     */
    function isUsedNonce(address _associatedAddress, uint256 _nonce) external view returns (bool) {
        return FermionStorage.metaTransaction().usedNonce[_associatedAddress][_nonce];
    }

    /**
     * @notice Manages allow list of functions that can be executed using metatransactions.
     *
     * Emits a FunctionsAllowlisted event if successful.
     *
     * Reverts if:
     * - Metatransaction region is paused
     * - Caller is not a protocol admin
     *
     * @param _functionNameHashes - a list of hashed function names (keccak256)
     * @param _isAllowlisted - new allowlist status
     */
    function setAllowlistedFunctions(
        bytes32[] calldata _functionNameHashes,
        bool _isAllowlisted
    ) external onlyRole(ADMIN) notPaused(FermionTypes.PausableRegion.MetaTransaction) nonReentrant {
        setAllowlistedFunctionsInternal(_functionNameHashes, _isAllowlisted);
    }

    /**
     * @notice Tells if function can be executed as meta transaction or not.
     *
     * @param _functionNameHash - hashed function name (keccak256)
     * @return isAllowlisted - allowlist status
     */
    function isFunctionAllowlisted(bytes32 _functionNameHash) external view returns (bool isAllowlisted) {
        return FermionStorage.metaTransaction().isAllowlisted[_functionNameHash];
    }

    /**
     * @notice Tells if function can be executed as meta transaction or not.
     *
     * @param _functionName - function name
     * @return isAllowlisted - allowlist status
     */
    function isFunctionAllowlisted(string calldata _functionName) external view returns (bool isAllowlisted) {
        return FermionStorage.metaTransaction().isAllowlisted[keccak256(abi.encodePacked(_functionName))];
    }

    /**
     * @notice Validates the nonce and function signature.
     *
     * Reverts if:
     * - Nonce is already used by the msg.sender for another transaction
     * - Function is not allowlisted to be called using metatransactions
     * - Function name does not match the bytes4 version of the function signature
     *
     * @param _functionName - the function name that we want to execute
     * @param _functionSignature - the function signature
     * @param _nonce - the nonce value of the transaction
     * @param _userAddress - the sender of the transaction
     */
    function validateTx(
        string calldata _functionName,
        bytes calldata _functionSignature,
        uint256 _nonce,
        address _userAddress
    ) internal view {
        FermionStorage.MetaTransaction storage mt = FermionStorage.metaTransaction();

        // Nonce should be unused
        if (mt.usedNonce[_userAddress][_nonce]) revert NonceUsedAlready();

        // Function must be allowlisted
        bytes32 functionNameHash = keccak256(abi.encodePacked(_functionName));
        if (!mt.isAllowlisted[functionNameHash]) revert FunctionNotAllowlisted();

        // Function name must correspond to selector
        bytes4 destinationFunctionSig = bytes4(_functionSignature);
        bytes4 functionNameSig = bytes4(functionNameHash);
        if (destinationFunctionSig != functionNameSig) revert InvalidFunctionName();
    }

    /**
     * @notice Returns hashed meta transaction.
     *
     * @param _metaTx - the meta-transaction struct.
     * @return the hash of the meta-transaction details
     */
    function hashMetaTransaction(FermionTypes.MetaTransaction memory _metaTx) internal pure returns (bytes32) {
        return
            keccak256(
                abi.encode(
                    META_TRANSACTION_TYPEHASH,
                    _metaTx.nonce,
                    _metaTx.from,
                    _metaTx.contractAddress,
                    keccak256(bytes(_metaTx.functionName)),
                    keccak256(_metaTx.functionSignature)
                )
            );
    }

    /**
     * @notice Executes the meta transaction.
     *
     * Reverts if:
     * - Any code executed in the signed transaction reverts
     *
     * @param _contractAddress - the address of the contract to be called, either this contract or one of FermionFNFTs
     * @param _userAddress - the sender of the transaction
     * @param _functionName - the name of the function to be executed
     * @param _functionSignature - the function signature
     * @param _nonce - the nonce value of the transaction
     */
    function executeTx(
        address _contractAddress,
        address _userAddress,
        string calldata _functionName,
        bytes calldata _functionSignature,
        uint256 _nonce
    ) internal returns (bytes memory) {
        // Store the nonce provided to avoid playback of the same tx
        FermionStorage.metaTransaction().usedNonce[_userAddress][_nonce] = true;

        // Invoke local function with an external call
        (bool success, bytes memory returnData) = _contractAddress.call{ value: msg.value }(
            abi.encodePacked(_functionSignature, _userAddress)
        );

        // If error, return error message
        if (!success) {
            if (returnData.length > 0) {
                // bubble up the error
                assembly {
                    revert(add(SLOT_SIZE, returnData), mload(returnData))
                }
            } else {
                // Reverts with default message
                revert FunctionCallFailed();
            }
        }

        emit MetaTransactionExecuted(_userAddress, msg.sender, _functionName, _nonce);
        return returnData;
    }

    /**
     * @notice Internal function that manages allow list of functions that can be executed using metatransactions.
     *
     * Emits a FunctionsAllowlisted event if successful.
     *
     * @param _functionNameHashes - a list of hashed function names (keccak256)
     * @param _isAllowlisted - new allowlist status
     */
    function setAllowlistedFunctionsInternal(bytes32[] calldata _functionNameHashes, bool _isAllowlisted) private {
        FermionStorage.MetaTransaction storage mt = FermionStorage.metaTransaction();

        // set new values
        for (uint256 i = 0; i < _functionNameHashes.length; ) {
            mt.isAllowlisted[_functionNameHashes[i]] = _isAllowlisted;

            unchecked {
                i++;
            }
        }

        // Notify external observers
        emit FunctionsAllowlisted(_functionNameHashes, _isAllowlisted, _msgSender());
    }
}
"
    },
    "contracts/protocol/interfaces/events/IFermionFractionsEvents.sol": {
      "content": "// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.24;

import { FermionTypes } from "../../domain/Types.sol";

/**
 * @title IFermionFractionsEvents
 *
 * @notice Defines events related fractions and buyout auctions.
 */
interface IFermionFractionsEvents {
    event Bid(
        uint256 indexed tokenId,
        address indexed from,
        uint256 newPrice,
        uint256 fractionsCount,
        uint256 bidAmount,
        uint256 epoch
    );
    event Redeemed(uint256 indexed tokenId, address indexed from, uint256 epoch);
    event Claimed(address indexed from, uint256 fractionsBurned, uint256 amountClaimed, uint256 epoch);
    event Voted(uint256 indexed tokenId, address indexed from, uint256 fractionAmount, uint256 epoch);
    event VoteRemoved(uint256 indexed tokenId, address indexed from, uint256 fractionAmount, uint256 epoch);
    event AuctionStarted(uint256 indexed tokenId, uint256 endTime, uint256 epoch);
    event Fractionalised(uint256 indexed tokenId, uint256 fractionsCount, uint256 epoch);
    event FractionsSetup(
        uint256 initialFractionsAmount,
        FermionTypes.BuyoutAuctionParameters buyoutAuctionParameters,
        uint256 epoch
    );
    event AdditionalFractionsMinted(uint256 additionalAmount, uint256 totalFractionsAmount, uint256 epoch);
    // Buyout Exit Price Governance Update Events
    event PriceUpdateProposalCreated(
        uint256 indexed proposalId,
        uint256 newExitPrice,
        uint256 votingDeadline,
        uint256 quorumRequired,
        uint256 epoch
    );
    event PriceUpdateProposalFinalized(uint256 indexed proposalId, bool success, uint256 epoch);
    event PriceUpdateVoted(
        uint256 indexed proposalId,
        address indexed voter,
        uint256 voteCount,
        bool votedYes,
        uint256 epoch
    );
    event PriceUpdateVoteRemoved(
        uint256 indexed proposalId,
        address indexed voter,
        uint256 votesRemoved,
        bool votedYes,
        uint256 epoch
    );
    event ExitPriceUpdated(uint256 newPrice, bool isOracleUpdate, uint256 epoch);
    event FractionsMigrated(address owners, uint256 fractionBalance);
}
"
    },
    "contracts/protocol/interfaces/events/IMetaTransactionEvents.sol": {
      "content": "// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.24;

/**
 * @title IMetaTransactionEvents
 *
 * @notice Defines events related to meta-transactions.
 */
interface IMetaTransactionEvents {
    event MetaTransactionExecuted(
        address indexed wallet,
        address indexed caller,
        string indexed functionName,
        uint256 nonce
    );
    event FunctionsAllowlisted(bytes32[] functionNameHashes, bool isAllowlisted, address indexed caller);
}
"
    },
    "contracts/protocol/interfaces/IBosonProtocol.sol": {
      "content": "// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.24;

/**
 * @title BosonInterface
 *
 * @notice Minimal interface to interact with the Boson Protocol.
 * Interface methods are copied here instead of being imported from bosonprotocol because of pragma incompatibility.
 */
interface IBosonProtocol {
    enum AuthTokenType {
        None,
        Custom,
        Lens,
        ENS
    }

    enum PriceType {
        Static, // Default should always be at index 0. Never change this value.
        Discovery
    }

    enum ExchangeState {
        Committed,
        Revoked,
        Canceled,
        Redeemed,
        Completed,
        Disputed
    }

    enum Side {
        Ask,
        Bid,
        Wrapper // Side is not relevant from the protocol perspective
    }

    enum OfferCreator {
        Seller, // Default should always be at index 0. Never change this value.
        Buyer
    }

    struct Seller {
        uint256 id;
        address assistant;
        address admin;
        address clerk; // Deprecated. Kept for backwards compatibility.
        address payable treasury;
        bool active;
        string metadataUri;
    }

    struct AuthToken {
        uint256 tokenId;
        AuthTokenType tokenType;
    }

    struct VoucherInitValues {
        string contractURI;
        uint256 royaltyPercentage;
        bytes32 collectionSalt;
    }

    struct Collection {
        address collectionAddress;
        string externalId;
    }

    struct Buyer {
        uint256 id;
        address payable account;
        bool active;
    }

    struct DisputeResolver {
        uint256 id;
        uint256 escalationResponsePeriod;
        address assistant;
        address admin;
        address clerk; // Deprecated. Kept for backwards compatibility.
        address payable treasury;
        string metadataUri;
        bool active;
    }

    struct DisputeResolverFee {
        address tokenAddress;
        string tokenName;
        uint256 feeAmount;
    }

    struct Offer {
        uint256 id;
        uint256 sellerId;
        uint256 price;
        uint256 sellerDeposit;
        uint256 buyerCancelPenalty;
        uint256 quantityAvailable;
        address exchangeToken;
        PriceType priceType;
        OfferCreator creator;
        string metadataUri;
        string metadataHash;
        bool voided;
        uint256 collectionIndex;
        RoyaltyInfo[] royaltyInfo;
        uint256 buyerId;
    }

    struct OfferDates {
        uint256 validFrom;
        uint256 validUntil;
        uint256 voucherRedeemableFrom;
        uint256 voucherRedeemableUntil;
    }

    struct OfferDurations {
        uint256 disputePeriod;
        uint256 voucherValid;
        uint256 resolutionPeriod;
    }

    struct DRParameters {
        uint256 disputeResolverId;
        address payable mutualizerAddress;
    }

    struct RoyaltyInfo {
        address payable[] recipients;
        uint256[] bps;
    }

    struct Exchange {
        uint256 id;
        uint256 offerId;
        uint256 buyerId;
        uint256 finalizedDate;
        ExchangeState state;
    }

    struct Voucher {
        uint256 committedDate;
        uint256 validUntilDate;
        uint256 redeemedDate;
        bool expired;
    }

    struct DisputeResolutionTerms {
        uint256 disputeResolverId;
        uint256 escalationResponsePeriod;
        uint256 feeAmount;
        uint256 buyerEscalationDeposit;
    }

    struct OfferFees {
        uint256 protocolFee;
        uint256 agentFee;
    }

    struct PriceDiscovery {
        uint256 price;
        Side side;
        address priceDiscoveryContract;
        address conduit;
        bytes priceDiscoveryData;
    }

    /**
     * @notice Creates a seller.
     *
     * @param _seller - the fully populated struct with seller id set to 0x0
     * @param _authToken - optional AuthToken struct that specifies an AuthToken type and tokenId that the seller can use to do admin functions
     * @param _voucherInitValues - the fully populated BosonTypes.VoucherInitValues struct
     */
    function createSeller(
        Seller memory _seller,
        AuthToken calldata _authToken,
        VoucherInitValues calldata _voucherInitValues
    ) external;

    /**
     * @notice Creates a buyer.
     *
     * @param _buyer - the fully populated struct with buyer id set to 0x0
     */
    function createBuyer(Buyer memory _buyer) external;

    /**
     * @notice Creates a dispute resolver.
     *
     * @param _disputeResolver - the fully populated struct with dispute resolver id set to 0x0
     * @param _disputeResolverFees - list of fees dispute resolver charges per token type. Zero address is native currency. See {BosonTypes.DisputeResolverFee}
     *                               feeAmount will be ignored because protocol doesn't yet support fees yet but DR still needs to provide array of fees to choose supported tokens
     * @param _sellerAllowList - list of ids of sellers that can choose this dispute resolver. If empty, there are no restrictions on which seller can choose it.
     */
    function createDisputeResolver(
        DisputeResolver memory _disputeResolver,
        DisputeResolverFee[] calldata _disputeResolverFees,
        uint256[] calldata _sellerAllowList
    ) external;

    /**
     * @notice Gets the next account id that can be assigned to an account.
     *
     * @dev Does not increment the counter.
     *
     * @return nextAccountId - the account id
     */
    function getNextAccountId() external view returns (uint256 nextAccountId);

    /**
     * @notice Gets the details about all seller's collections.
     * In case seller has too many collections and this runs out of gas, please use getSellersCollectionsPaginated.
     *
     * @param _sellerId - the id of the seller to check
     * @return defaultVoucherAddress - the address of the default voucher contract for the seller
     * @return additionalCollections - an array of additional collections that the seller has created
     */
    function getSellersCollections(
        uint256 _sellerId
    ) external view returns (address defaultVoucherAddress, Collection[] memory additionalCollections);

    /**
     * @notice Creates an offer.
     *
     *
     * @param _offer - the fully populated struct with offer id set to 0x0 and voided set to false
     * @param _offerDates - the fully populated offer dates struct
     * @param _offerDurations - the fully populated offer durations struct
     * @param _drParameters - the id of chosen dispute resolver (can be 0) and mutualizer address (0 for self-mutualization)
     * @param _agentId - the id of agent
     * @param _feeLimit - the maximum fee that seller is willing to pay per exchange (for static offers)
     */
    function createOffer(
        Offer memory _offer,
        OfferDates calldata _offerDates,
        OfferDurations calldata _offerDurations,
        DRParameters calldata _drParameters,
        uint256 _agentId,
        uint256 _feeLimit
    ) external;

    /**
     * @notice Adds DisputeResolverFees to an existing dispute resolver.
     *
     * @param _disputeResolverId - id of the dispute resolver
     * @param _disputeResolverFees - list of fees dispute resolver charges per token type. Zero address is native currency. See {BosonTypes.DisputeResolverFee}
     *                               feeAmount will be ignored because protocol doesn't yet support fees yet but DR still needs to provide array of fees to choose supported tokens
     */
    function addFeesToDisputeResolver(
        uint256 _disputeResolverId,
        DisputeResolverFee[] calldata _disputeResolverFees
    ) external;

    /**
     * @notice Gets the next offer id.
     *
     * @dev Does not increment the counter.
     *
     * @return nextOfferId - the next offer id
     */
    function getNextOfferId() external view returns (uint256 nextOfferId);

    /**
     * @notice Receives funds from the caller, maps funds to the seller id and stores them so they can be used during the commitToOffer.
     *
     * @param _sellerId - id of the seller that will be credited
     * @param _tokenAddress - contract address of token that is being deposited (0 for native currency)
     * @param _amount - amount to be credited
     */
    function depositFunds(uint256 _sellerId, address _tokenAddress, uint256 _amount) external payable;

    /**
     * @notice Reserves a range of vouchers to be associated with an offer
     *
     * @param _offerId - the id of the offer
     * @param _length - the length of the range
     * @param _to - the address to send the pre-minted vouchers to (contract address or contract owner)
     */
    function reserveRange(uint256 _offerId, uint256 _length, address _to) external;

    /**
     * @notice Gets the id that will be assigned to the next exchange.
     *
     * @return nextExchangeId - the next exchange id
     */
    function getNextExchangeId() external view returns (uint256 nextExchangeId);

    /**
     * @notice Commits to a price discovery offer (first step of an exchange).
     *
     * @param _buyer - the buyer's address (caller can commit on behalf of a buyer)
     * @param _tokenIdOrOfferId - the id of the offer to commit to or the id of the voucher (if pre-minted)
     * @param _priceDiscovery - price discovery data (if applicable). See BosonTypes.PriceDiscovery
     */
    function commitToPriceDiscoveryOffer(
        address payable _buyer,
        uint256 _tokenIdOrOfferId,
        PriceDiscovery calldata _priceDiscovery
    ) external payable;

    /**
     * @notice Redeems a voucher.
     *
     * @param _exchangeId - the id of the exchange
     */
    function redeemVoucher(uint256 _exchangeId) external;

    /**
     * @notice Completes an exchange.
     *
     * @param _exchangeId - the id of the exchange to complete
     */
    function completeExchange(uint256 _exchangeId) external;

    /**
     * @notice Withdraws the specified funds. Can be called for seller, buyer or agent.
     *
     * @param _entityId - id of entity for which funds should be withdrawn
     * @param _tokenList - list of contract addresses of tokens that are being withdrawn
     * @param _tokenAmounts - list of amounts to be withdrawn, corresponding to tokens in tokenList
     */
    function withdrawFunds(uint256 _entityId, address[] calldata _tokenList, uint256[] calldata _tokenAmounts) external;

    /**
     * @notice Gets the protocol fee percentage.
     *
     * @return the protocol fee percentage
     */
    function getProtocolFeePercentage() external view returns (uint256);

    /**
     * @notice Gets the protocol fee percentage based on protocol fee table
     *
     * @dev This function calculates the protocol fee percentage for specific token and price.
     * If the token has a custom fee table configured, it returns the corresponding fee percentage
     * for the price range. If the token does not have a custom fee table, it falls back
     * to the default protocol fee percentage.
     *
     * Reverts if the exchange token is BOSON.
     *
     * @param _exchangeToken - The address of the token being used for the exchange.
     * @param _price - The price of the item or service in the exchange.
     *
     * @return the protocol fee percentage for given price and exchange token
     */
    function getProtocolFeePercentage(address _exchangeToken, uint256 _price) external view returns (uint256);

    /**
     * @notice Retrieves the protocol fee percentage for a given exchange token and price.
     *
     * @dev This function calculates the protocol fee based on the token and price.
     * If the token has a custom fee table, it applies the corresponding fee percentage
     * for the price range. If the token does not have a custom fee table, it falls back
     * to the default protocol fee percentage. If the exchange token is $BOSON,
     * this function returns the flatBoson fee
     *
     * @param _exchangeToken - The address of the token being used for the exchange.
     * @param _price - The price of the item or service in the exchange.
     *
     * @return The protocol fee amount based on the token and the price.
     */
    function getProtocolFee(address _exchangeToken, uint256 _price) external view returns (uint256);

    /**
     * @notice Gets the flat protocol fee for exchanges in $BOSON.
     *
     * @return the flat fee taken for exchanges in $BOSON
     */
    function getProtocolFeeFlatBoson() external view returns (uint256);

    /**
     * @notice Gets the Boson Token (ERC-20 contract) address.
     *
     * @return the Boson Token (ERC-20 contract) address
     */
    function getTokenAddress() external view returns (address payable);
}

interface IBosonVoucher {
    /**
     * @notice Pre-mints all or part of an offer's reserved vouchers.
     *
     * For small offer quantities, this method may only need to be
     * called once.
     *
     * But, if the range is large, e.g., 10k vouchers, block gas limit
     * could cause the transaction to fail. Thus, in order to support
     * a batched approach to pre-minting an offer's vouchers,
     * this method can be called multiple times, until the whole
     * range is minted.
     *
     * A benefit to the batched approach is that the entire reserved
     * range for an offer need not be pre-minted at one time. A seller
     * could just mint batches periodically, controlling the amount
     * that are available on the market at any given time, e.g.,
     * creating a pre-minted offer with a validity period of one year,
     * causing the token range to be reserved, but only pre-minting
     * a certain amount monthly.
     *
     * Caller must be contract owner (seller assistant address).
     *
     * Reverts if:
     * - Offer id is not associated with a range
     * - Amount to mint is more than remaining un-minted in range
     * - Too many to mint in a single transaction, given current block gas limit
     *
     * @param _offerId - the id of the offer
     * @param _amount - the amount to mint
     */
    function preMint(uint256 _offerId, uint256 _amount) 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 caller.
     *
     * Emits an {ApprovalForAll} event.
     */
    function setApprovalForAll(address operator, bool approved) external;
}
"
    },
    "contracts/protocol/interfaces/IFermionFNFT.sol": {
      "content": "// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.24;

import { FermionTypes } from "../domain/Types.sol";
import { IFermionWrapper } from "../interfaces/IFermionWrapper.sol";
import { IFermionFractions } from "../interfaces/IFermionFractions.sol";

/**
 * @title FermionWrapper interface
 *
 * A set of methods to interact with the FermionWrapper contract.
 */
interface IFermionFNFT is IFermionWrapper, IFermionFractions {
    /**
     * @notice Initializes the contract
     *
     * Reverts if:
     * - Contract is already initialized
     *
     * @param _voucherAddress The address of the Boson Voucher contract
     * @param _owner The address of the owner
     * @param _exchangeToken The address of the exchange token
     * @param _offerId The offer id
     * @param _metadataUri The metadata URI, used for all tokens and contract URI
     * @param _tokenMetadata - optional token metadata (name and symbol)
     */
    function initialize(
        address _voucherAddress,
        address _owner,
        address _exchangeToken,
        uint256 _offerId,
        string memory _metadataUri,
        FermionTypes.TokenMetadata memory _tokenMetadata
    ) external;

    /**
     * @notice Burns the token and returns the voucher owner
     *
     * Reverts if:
     * - Caller is not the Fermion Protocol
     * - Token is not in the Unverified state
     *
     * @param _tokenId The token id.
     */
    function burn(uint256 _tokenId) external returns (address wrappedVoucherOwner);

    /**
     * @notice Pushes the F-NFT from unverified to verified
     *
     * Reverts if:
     * - Caller is not the Fermion Protocol
     * - The new token state is not consecutive to the current state
     *
     * N.B. Not checking if the new state is valid, since the caller is the Fermion Protocol, which is trusted
     *
     * @param _tokenId The token id.
     */
    function pushToNextTokenState(uint256 _tokenId, FermionTypes.TokenState _newState) external;

    function tokenState(uint256 _tokenId) external view returns (FermionTypes.TokenState);

    /**
     * @notice Returns the address of the ERC20 clone for a specific epoch
     * Users should interact with this contract directly for ERC20 operations
     *
     * @param _epoch The epoch
     * @return The address of the ERC20 clone
     */
    function getERC20FractionsClone(uint256 _epoch) external view returns (address);

    /**
     * @notice Returns the address of the ERC20 clone for the current epoch
     * Users should interact with this contract directly for ERC20 operations
     *
     * @return The address of the ERC20 clone
     */
    function getERC20FractionsClone() external view returns (address);
}
"
    },
    "contracts/protocol/interfaces/IFermionFractions.sol": {
      "content": "// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.24;

import { FermionTypes } from "../domain/Types.sol";
import { IFermionFractionsEvents } from "./events/IFermionFractionsEvents.sol";

/**
 * @title FermionWrapper interface
 *
 * A set 

Tags:
ERC721, ERC165, Multisig, Non-Fungible, Voting, Upgradeable, Multi-Signature, Factory, Oracle|addr:0xffea4ec2be94a42c20ca2d899a7086770e1a9206|verified:true|block:23632282|tx:0xe13b52a1094918ea5e305301496b5c97a90e615377d4b329f9dee5f79e84c7ef|first_check:1761241136

Submitted on: 2025-10-23 19:38:59

Comments

Log in to comment.

No comments yet.