Hartii developer docs

HartiiLabs

Launch token

LaunchToken is the original-generation HartiiLabs clone token. Source: contracts/contracts/LaunchToken.sol in the hartii-labs repo. Original implementation address: 0x0059af4b7441e15E06e4686bd2f4CC5dfF5AAA0A — each token of that generation is a clone at its own address.

Non-standard behavior an integrator must know

  • There is no mint() function anywhere in this contract's ABI. The full fixed supply is minted exactly once, atomically, inside initialize(). Supply can never be inflated after launch; totalSupply can only ever go down, via burn().
  • transfer/transferFrom are the plain textbook ERC-20 implementation — no fee-on-transfer, no blacklist, no pause, no owner-only transfer hook of any kind. A creator cannot quietly disable sells because there is no hook point in the contract to add one.
  • The only non-standard transfer behavior is the creator vesting gate below, which applies only to the creator address.

ERC-20 surface

FunctionSignatureNotes
name()string public nameSet once at initialize().
symbol()string public symbolSet once at initialize().
decimals()uint8 public constant decimalsAlways 18.
totalSupply()uint256 public totalSupplyFixed at mint; only decreases via burn().
balanceOf(address account)external view returns (uint256)Standard.
allowance(address owner_, address spender)external view returns (uint256)Standard.
approve(address spender, uint256 amount)external returns (bool)Standard; no increaseAllowance/decreaseAllowance helpers exist.
transfer(address to, uint256 amount)external returns (bool)Reverts "Zero to" (no burn-via-zero-address — use burn() instead), "Insufficient balance", and the creator vesting gate below if msg.sender == creator.
transferFrom(address from, address to, uint256 amount)external returns (bool)Same reverts as transfer, plus "Insufficient allowance". An allowance of type(uint256).max is treated as infinite and never decremented (standard gas-saving pattern).

burn()

solidity
function burn(uint256 amount) external

Any holder may destroy their own tokens: reduces their balance and totalSupply, emits Transfer(msg.sender, address(0), amount). Reverts "Insufficient balance" if the caller doesn't hold amount. If the caller is the creator, it is subject to the exact same vesting gate as a transfer ("Creator allocation still locked").

Creator vesting

Set once at initialize(), driven by the factory's creatorLockDuration setting at the time of that launch:

FieldMeaning
creator()The launch's creator address.
creatorAllocation()Total tokens allocated to the creator (the locked pool), minted directly to their balance at launch.
creatorClaimed()Unused bookkeeping field — vesting is enforced purely by the transfer gate below, not by a separate claim step.
lockedUntil()block.timestamp + creatorLockDuration at launch time. Before this timestamp, zero of the creator's allocation is transferable.
VESTING_DURATIONConstant, 7 days. Linear release window starting at lockedUntil.
unlockedCreatorAllocation()view — amount of the original allocation unlocked so far. 0 before lockedUntil; linear from 0 to creatorAllocation over the 7 days after; creatorAllocation (fully unlocked) once elapsed >= VESTING_DURATION.
solidity
function unlockedCreatorAllocation() public view returns (uint256) {
    if (block.timestamp < lockedUntil) return 0;
    uint256 elapsed = block.timestamp - lockedUntil;
    if (elapsed >= VESTING_DURATION) return creatorAllocation;
    return (creatorAllocation * elapsed) / VESTING_DURATION;
}

The transfer gate — applied inside _transfer (and inside burn()), on the creator address specifically, gated on the original allocation, not the creator's current balance (so it can't be bypassed by routing through another contract or by having already sold part of the position):

solidity
if (from == creator) {
    uint256 locked = unlockedCreatorAllocation() < creatorAllocation
        ? creatorAllocation - unlockedCreatorAllocation()
        : 0;
    require(fromBalance - amount >= locked, "Creator allocation still locked");
}

In plain terms: the creator can always move tokens down to their currently-locked remainder, never below it. With creatorLockDuration = 0, the 7-day linear release starts immediately at launch.

Events

EventIndexed fieldsNon-indexed fieldsEmitted by
Transferfrom, tovalueinitialize() (the two initial mints, from address(0)), transfer, transferFrom, burn (to address(0))
Approvalowner, spendervalueapprove
CreatorAllocationClaimedcreatoramountDeclared in the ABI but never emitted anywhere in this contract's code — see Unverified below.

Provenance fields

FieldMeaning
factory()The TokenFactory that deployed and initialized this clone (msg.sender inside initialize()).
metadataHash()The bytes32 commitment supplied at launch; see launch images below for the composer metadata format.

Launch images

The launch composer accepts an optional PNG, JPEG, or WebP image. Drop, paste, or choose it in Image, then drag to reposition the square crop. While Preparing your image… is visible, launch is blocked. Changing the file or crop replaces the pending preparation; only the latest result is accepted. Remove clears the image and invalidates pending work, so an earlier result cannot put it back.

Larger source images can be cropped and shrunk in the browser. The final launch-image payload must be at most 1 MiB (1,048,576 bytes). If preparation cannot produce an acceptable file, choose a smaller image or remove it before launching. An image error blocks launch.

Before submitting a paid launch, the composer requests a free wallet message signature and saves the prepared image with its token information. A selected-image save must succeed first. If it fails, the composer stops before requesting the paid launch and keeps the form and image on the open page for retry. No launch fee is charged by that stopped attempt. Keep the page open; this is not a promise that a reload or navigation restores an unsaved browser form. Without an image, an optional description/link save failure can still allow launch to continue without those fields. Rejecting the message signature cancels the attempt.

Save acknowledgement and image delivery

For integrations, POST /api/token-meta accepts the signed draft and optional logo file as multipart form data. HTTP success alone is insufficient: require ok: true and stored: true, and require logoStored: true whenever a file was submitted. For example:

json
{"ok":true,"draftId":"example-draft","stored":true,"logoStored":true,"logoCid":null}

The platform saves the validated image bytes and creator-associated metadata before starting a bounded, optional IPFS pin attempt. A null logoCid does not mean the acknowledged image was lost. Token images are served through /api/token-logo/<token-address> from platform storage, with IPFS as a fallback source. An IPFS outage does not need to delay an already-saved image or the launch. Unlaunched drafts expire after seven days; a recognized, creator-matching launch promotes its stored image and metadata for continued use.

The image endpoint can find saved bytes through the token's metadata hash even if its first indexed record lacks logo flags. Recovery checks its creator against the indexed creator, checks the stored image digest when present, and retains verified metadata for continued use. If a known CID cannot currently be fetched, it answers 503 temporarily unavailable without caching that failure as missing artwork. An unresolved image with a metadata commitment has a 30-second edge miss cache, rather than the five-minute cache used for an ordinary missing image. While a token avatar stays mounted, a failed platform-image request gets at most two automatic retries: after 30 seconds, then after another 60 seconds if it fails again. The avatar keeps its reserved frame during loading and retries. These bounds allow delayed images to appear without promising that every unavailable image will recover.

New composer commitments hash canonical metadata containing the creator address and a digest of the prepared image bytes, alongside the name, symbol, description and social links. Changing the creator or image changes the commitment. Existing on-chain commitments and their stored records remain readable without conversion.

An older token has no image

Connect the token's creator wallet, open its HartiiLabs token page and select Edit info. Under Logo, choose the image and press Sign & set logo. The server checks the signature against the indexed token creator and binds it to the uploaded bytes and file type. This updates the token's off-chain image; it does not require launching the token again.

If the original bytes are absent from both platform storage and reachable IPFS content, they cannot be reconstructed from the metadata hash. Supply the original file or another image through the creator editor. HartiiLabs image delivery does not guarantee that a third-party wallet will display the same artwork; its metadata and cache behavior require separate verification.

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.