Hartii developer docs

Gallery

Contract reference

All addresses below are live on Quai mainnet (Cyprus-1, chain 9), confirmed with quai_getCode. Five of the six trading venues (HartiiMarketplaceV8, HartiiBundleMarketplace, HartiiAuctionHouse, HartiiGaslessMarketplace, HartiiMarketplace1155) share one admin trust model — it's described once at the end rather than five times.

HartiiFactoryV7Clone — collection factory

0x007d00B9752d852658A03167b825d62b68c375Fc

Deploys new NFT collections as EIP-1167 minimal-proxy clones of HartiiCollectionV7Clone (~95% cheaper than a full deploy). Each clone is initialized in the same transaction it's created in, so there's no window where an uninitialized clone could be front-run.

solidity
function deployCollection(
    string calldata _name,
    string calldata _symbol,
    uint256 _mintPrice,
    uint256 _maxSupply,
    string calldata _baseURI,
    uint256 _creatorThreshold,
    uint256 _royaltyBps,       // 0–800 (0%–8%), immutable after deploy
    uint256 _maxPerWallet      // 0 = unlimited
) external;

function collectionCount() external view returns (uint256);
function collections(uint256) external view returns (address); // public array
function isHartiiCollection(address) external view returns (bool);

Event: CollectionDeployed(address indexed collection, address indexed creator, string name, string symbol, uint256 royaltyBps)

Reverts: "Zero supply", "Zero price", "Empty URI", "URI too long" (>512 bytes), "Invalid name length" (empty or >128 bytes), "Invalid symbol length" (empty or >32 bytes), "Royalty max 8%" (>800 bps), "Clone failed" (CREATE failure).

Trust model: treasury (receives 5% of every primary mint across all collections deployed by this factory) and marketplace (the address pre-approved on every new clone so a creator's first listing is one signature) are both set once in the constructor. Neither has a setter — there is no function anywhere in this contract that changes them after deployment. The factory holds no funds itself (mint payments go straight from buyer to the collection clone, per its own Phase 1/2 split — see below) and has no admin/owner role at all.

HartiiCollectionV7Clone — the collection template

Every collection created through the factory above is its own deployed instance of this contract (an EIP-1167 clone), not a shared contract — there is no single collection address.

Purpose: ERC-721 collection with a built-in two-phase revenue model. Phase 1: 5% platform fee, 95% to the creator, until the collection's mint revenue crosses creatorThreshold. Phase 2: 5% platform fee, 95% split proportionally among holders (a per-share accumulator, accRevenuePerShare) — this only activates if creatorThreshold > 0; if it's 0, all revenue after the 5% fee goes to the creator forever, no Phase 2.

solidity
function mint() external payable;
function mintBatch(uint256 quantity) external payable;
function totalMinted() external view returns (uint256);
function totalSupply() external view returns (uint256);
function royaltyInfo(uint256, uint256 salePrice) external view returns (address receiver, uint256 royaltyAmount); // EIP-2981
function isPhaseTwo() external view returns (bool);
function isClaimable() external view returns (bool);
function pendingRevenue(uint256 tokenId) external view returns (uint256);
function claimRevenue(uint256 tokenId) external;
function claimRevenueBatch(uint256[] calldata tokenIds) external;
function withdrawCreatorPayments() external;   // pulls any deferred Phase-1 creator payout
function withdrawPendingRevenue() external;    // pulls any deferred Phase-2 holder payout
function burn(uint256 tokenId) external;
function setBaseURI(string calldata) external;     // creator only
function freezeMetadata() external;                // creator only, one-way
function transferCreator(address) external;         // creator only, two-step
function acceptCreator() external;

Standard ERC-721 (ownerOf, balanceOf, approve, transferFrom, etc.) plus BatchMetadataUpdate (ERC-4906) support.

Trust model: there is no platform-owner role on the collection itself — only a creator role (set to the deployer at initialize()), and its only powers are setBaseURI, freezeMetadata (one-way, no un-freeze), and handing the creator role to a new address via a two-step transfer. The creator cannot pause minting, cannot change the royalty (immutable, fixed at deploy from the factory call), cannot change the mint price after deploy, and cannot withdraw anyone else's pending revenue. Royalty payout recipient/amount is computed by royaltyInfo and paid by whichever marketplace venue honors EIP-2981 (all of them below do).

Marketplace venues — shared trust model

HartiiMarketplaceV8, HartiiBundleMarketplace, HartiiAuctionHouse, HartiiGaslessMarketplace and HartiiMarketplace1155 are five independent contracts with the same admin shape (only the trading mechanics differ):

ContractAddressMechanicLive feeBps
HartiiMarketplaceV80x006792…7e867Fixed-price listing + offers250 (2.5%)
HartiiBundleMarketplace0x0028E7…7edAMulti-token bundle, one price250 (2.5%)
HartiiAuctionHouse0x0068d6…019cEnglish auction, reserve + min increment250 (2.5%)
HartiiGaslessMarketplace0x005f63…a410fEIP-712 signed order, buyer pays gas250 (2.5%)
HartiiMarketplace11550x00137c…fA04Quantity-aware ERC-1155 listing250 (2.5%)

(feeBps values above are each contract's constructor default read from source — not a live on-chain read at doc time; the cap below always holds regardless.)

Core functions (naming varies slightly per venue — see each .sol file for exact signatures): list… / listBundle / createAuction, buy… / buyBundle / bid / fulfillOrder, cancel…, updatePrice / updateBundlePrice, withdraw() (pull-based — sellers/bidders claim their own proceeds/refunds rather than being pushed funds), plus getActiveListings/getActiveBundles/getActiveAuctions style paginated views.

Common events: a Listed/Sold/Cancelled/PriceUpdated (or auction/bundle/order equivalents) family per venue, plus the shared admin events below.

Trust model — identical across all five contracts:

  • admin, a single address, two-step transferable (transferAdmin → pending, then acceptAdmin from the pending address) — never a one-step hijack-by-typo.
  • Fee cap is hard-coded and enforced on-chain. setFeeBps(uint256) is onlyAdmin and every contract requires _newFeeBps <= MAX_FEE_BPS, where MAX_FEE_BPS = 500 (5%) is a constant — admin cannot set a fee above 5% no matter what; there is no separate function that bypasses this cap.
  • pause() / unpause(), onlyAdmin — stops new listings/bids/orders (existing cancellations and pull-withdrawals still work while paused, per whenNotPaused placement on each contract).
  • rescueETH(), onlyAdmin, present on HartiiMarketplaceV8 and HartiiMarketplace1155 (the bundle/auction/gasless venues have their own withdraw()-based sweep instead) — it can only ever move the contract's balance above what's escrowed for live offers/listings/bids (balance - reserved), and it always pays out to treasury, never to an arbitrary admin-supplied address. Admin cannot use it to seize a buyer's escrowed offer or a seller's pending sale proceeds.
  • No mint/upgrade/selfdestruct anywhere in these contracts. Admin's full power set is: set the fee (≤5%), pause/unpause, sweep unescrowed dust to treasury, and hand off the admin role. Admin cannot change who receives a specific sale's proceeds, cannot cancel someone else's listing to relist it themselves, and cannot touch escrowed funds still owed to a buyer/seller/bidder.

HartiiMarketplaceV8 additionally exposes setStakingVault(address) (onlyAdmin) which only records purchases to an external staking-rewards contract on sale — it has no effect on price, fee, or custody, and it targets 0x000f9785e53597680Bb74ffF4f78801648333f61 (HartiiStakingVault), which — see index — has no live bytecode on mainnet, so this call currently has no observable effect even when configured.

HartiiCurationRegistry

0x000404Cd0833465bCe3E345454B901b69B5ff51C

Purpose: deliberately narrow — v1 supports exactly one editorial label (isHorologyDeploy), a boolean per collection address marking it as an official Hartii-curated "Horology" deploy, for the frontend to badge. No generic tagging surface exists on-chain yet.

solidity
function isHorologyDeploy(address collection) external view returns (bool);
function horologyDeployCount() external view returns (uint256);
function horologyDeployAt(uint256 index) external view returns (address);
function horologyDeploys() external view returns (address[] memory);
function setHorologyDeploy(address collection, bool enabled) external; // onlyAdmin
function transferAdmin(address newAdmin) external;                    // onlyAdmin, two-step
function acceptAdmin() external;

Events: HorologyDeploySet(address indexed collection, bool enabled, address indexed operator), AdminTransferInitiated, AdminTransferred.

Errors (custom, not string reverts): NotAdmin(address caller), NotPendingAdmin(address caller), ZeroAddress(), IndexOutOfBounds(uint256 index).

Trust model: the entire contract is a boolean label with no financial function at all — it cannot move funds, mint, or affect any marketplace's trading logic directly (the frontend reads this registry to decide what badge to render). Admin's only power is toggling the label on a collection address; the same two-step admin transfer pattern as the marketplace venues applies.

Quai Network mainnet · chain 9 · Cyprus-1. Figures marked "read on" a date were read from the chain that day; re-read before relying on them.