Contracts
Source: hartii-labs/contracts/contracts/swap/ (Solidity 0.8.20, GPL-3.0-or-later for the V2 port).
HartiiSwapFactory
constructor(address feeToSetter, address feeTo) — deployed with both set to the treasury.
| Function | Who | Description |
|---|---|---|
createPair(address tokenA, address tokenB) → address pair | anyone | Deploys a pair with CREATE, initializes it with the sorted tokens, records it both ways. Reverts HartiiSwap: IDENTICAL_ADDRESSES, HartiiSwap: ZERO_ADDRESS, HartiiSwap: PAIR_EXISTS. The router calls this automatically on the first addLiquidity. |
getPair(address, address) → address | view | Pair for either token order; zero if none. The only way to find a pair. |
allPairs(uint256) → address, allPairsLength() | view | Enumeration in creation order. |
feeTo(), feeToSetter(), pendingFeeToSetter() | view | Protocol-fee recipient and its controller. |
setFeeTo(address) | feeToSetter | Zero turns the protocol fee off. |
proposeFeeToSetter(address) / acceptFeeToSetter() | setter / proposed | Two-step handover. |
Events: PairCreated(token0 indexed, token1 indexed, pair, index) (index is 1-based length after
push, as in V2), FeeToChanged, FeeToSetterProposed, FeeToSetterAccepted.
HartiiSwapPair
Uniswap V2 pair, unchanged in behaviour. Also the LP ERC-20 (HSLP).
| Function | Description |
|---|---|
token0(), token1() | Sorted pair tokens (token0 < token1 by address). |
getReserves() → (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast) | Current reserves. Use these, never raw token balances, to price. |
price0CumulativeLast(), price1CumulativeLast() | UQ112.112 TWAP accumulators (updated on the first interaction per timestamp). |
kLast() | reserve0·reserve1 after the last liquidity event while the protocol fee is on. |
mint(address to) → liquidity | Low-level: mints LP for tokens already transferred in. Use the router. |
burn(address to) → (amount0, amount1) | Low-level: burns LP already transferred in. Use the router. |
swap(amount0Out, amount1Out, to, data) | Low-level optimistic swap; with non-empty data calls IHartiiSwapCallee(to).hartiiSwapCall(sender, amount0Out, amount1Out, data) (flash swap). Must end with balance·1000 − amountIn·3 satisfying the K check. |
skim(address to), sync() | Standard V2 recovery helpers. |
ERC-20 + permit(owner, spender, value, deadline, v, r, s) | EIP-2612; DOMAIN_SEPARATOR uses name HartiiSwap LP, version 1, and the chain id at deploy. |
Reverts: HartiiSwap: LOCKED (reentrancy), INSUFFICIENT_LIQUIDITY_MINTED,
INSUFFICIENT_LIQUIDITY_BURNED, INSUFFICIENT_OUTPUT_AMOUNT, INSUFFICIENT_LIQUIDITY,
INVALID_TO, INSUFFICIENT_INPUT_AMOUNT, K, OVERFLOW, TRANSFER_FAILED, EXPIRED,
INVALID_SIGNATURE, FORBIDDEN.
Events: Mint(sender, amount0, amount1), Burn(sender, amount0, amount1, to),
Swap(sender, amount0In, amount1In, amount0Out, amount1Out, to), Sync(reserve0, reserve1),
plus ERC-20 Transfer/Approval.
HartiiSwapRouter
factory() and WETH() (WQUAI). All state-changing functions take a deadline (unix seconds) and
revert HartiiSwapRouter: EXPIRED after it. Pairs are resolved with factory.getPair; a missing hop
reverts HartiiSwapLibrary: PAIR_NOT_FOUND.
Liquidity
| Function | Notes |
|---|---|
addLiquidity(tokenA, tokenB, amountADesired, amountBDesired, amountAMin, amountBMin, to, deadline) | Creates the pair if missing. First deposit uses both desired amounts as-is (it sets the price); later deposits are fitted to the pool ratio and must meet the mins (INSUFFICIENT_A_AMOUNT / INSUFFICIENT_B_AMOUNT). |
addLiquidityETH(token, amountTokenDesired, amountTokenMin, amountETHMin, to, deadline) payable | Same with native QUAI as the second side; excess QUAI is refunded. |
removeLiquidity(tokenA, tokenB, liquidity, amountAMin, amountBMin, to, deadline) | Requires LP approval to the router. |
removeLiquidityETH(token, liquidity, amountTokenMin, amountETHMin, to, deadline) | Pays the QUAI side as native QUAI. |
removeLiquidityWithPermit / removeLiquidityETHWithPermit (+ …, bool approveMax, uint8 v, bytes32 r, bytes32 s) | Approve via signature in the same call. |
removeLiquidityETHSupportingFeeOnTransferTokens (+ WithPermit) | For taxed tokens. |
Swaps
| Function | Direction |
|---|---|
swapExactTokensForTokens(amountIn, amountOutMin, path, to, deadline) | exact in |
swapTokensForExactTokens(amountOut, amountInMax, path, to, deadline) | exact out |
swapExactETHForTokens(amountOutMin, path, to, deadline) payable | QUAI in, path[0] = WQUAI |
swapTokensForExactETH(amountOut, amountInMax, path, to, deadline) | QUAI out |
swapExactTokensForETH(amountIn, amountOutMin, path, to, deadline) | QUAI out |
swapETHForExactTokens(amountOut, path, to, deadline) payable | exact out, refunds excess QUAI |
…SupportingFeeOnTransferTokens variants of the three exact-in swaps | for taxed tokens |
Reverts: INSUFFICIENT_OUTPUT_AMOUNT (slippage), EXCESSIVE_INPUT_AMOUNT, INVALID_PATH,
TransferHelper: TRANSFER_FROM_FAILED (usually a missing approval to the router).
Pure/view helpers: quote(amountA, reserveA, reserveB), getAmountOut(amountIn, reserveIn, reserveOut), getAmountIn(amountOut, reserveIn, reserveOut), getAmountsOut(amountIn, path),
getAmountsIn(amountOut, path). getAmountsOut equals the real swap to the wei (verified on
mainnet 2026-09-23: 0.5 QUAI → 162.533099935654199908 QAXE, quote = swap = formula).
HartiiSwapLens
Stateless reads for UIs; no funds, no owner.
| Function | Returns |
|---|---|
pool(address pair) | PoolInfo { pair, token0 {token, symbol, decimals}, token1 {…}, reserve0, reserve1, totalSupply } |
pools(address factory, uint256 start, uint256 count) | PoolInfo[] for allPairs[start … start+count), clipped to what exists |
positions(address factory, address user, uint256 start, uint256 count) | Position { pair, lpBalance, totalSupply, amount0, amount1 }[] — amount0/1 are the user's current share of reserves; zero balances included so indices line up with pools |
A token whose symbol() reverts is returned with an empty symbol and 18 decimals rather than failing
the whole call. Keep count modest (e.g. 50) — the call is unbounded by design.
Gas (measured)
| Operation | Hardhat | Quai (Orchard / mainnet) |
|---|---|---|
| Deploy factory / router / lens | — | 2.24M / 2.98M / 0.79M (Orchard) |
addLiquidityETH, first (creates pair) | 1,994,354 | 2,001,846 (Orchard), 2,170,432 (mainnet QAXE pool) |
addLiquidityETH, later | 164,214 | 152,259 |
swapExactETHForTokens | 143,626 | 140,176 |
swapExactTokensForETH | — | 128,602 |
removeLiquidityETH | 191,726 | 183,446 |