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.
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.
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):
| Contract | Address | Mechanic | Live feeBps |
|---|---|---|---|
HartiiMarketplaceV8 | 0x006792…7e867 | Fixed-price listing + offers | 250 (2.5%) |
HartiiBundleMarketplace | 0x0028E7…7edA | Multi-token bundle, one price | 250 (2.5%) |
HartiiAuctionHouse | 0x0068d6…019c | English auction, reserve + min increment | 250 (2.5%) |
HartiiGaslessMarketplace | 0x005f63…a410f | EIP-712 signed order, buyer pays gas | 250 (2.5%) |
HartiiMarketplace1155 | 0x00137c…fA04 | Quantity-aware ERC-1155 listing | 250 (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, thenacceptAdminfrom the pending address) — never a one-step hijack-by-typo.- Fee cap is hard-coded and enforced on-chain.
setFeeBps(uint256)isonlyAdminand every contract requires_newFeeBps <= MAX_FEE_BPS, whereMAX_FEE_BPS = 500(5%) is aconstant— 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, perwhenNotPausedplacement on each contract).rescueETH(),onlyAdmin, present onHartiiMarketplaceV8andHartiiMarketplace1155(the bundle/auction/gasless venues have their ownwithdraw()-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 totreasury, 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.
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.