Heylogram TV Ecosystem

A Web3 economy built on Polygon with 6 tokens, 16 smart contracts and a B2B platform. Domain NFTs carry identity, the Garura seasonal pool carries rewards, the URA click game carries scale, and the partner system carries discovery.

Version: 1.0 · 2026-05Network: Polygon MainnetContracts: 16 .sol · Solidity 0.8.20–0.8.24Stack: Next.js 16 · wagmi v2 · RainbowKit · Firestore
Note (2026-05-16): The Quests system, the Garura seasonal reward pool, and the Partner / B2B platform are currently shelved. These modules are on hold for further testing and development; design references remain below but the ecosystem is not live. Active modules: Domain NFT, URA click game, DEX / Locker / LuckPool. They'll come back online once mature.

01Vision

Heylogram TV answers two questions: "How does digital identity become individual?" and "How does a Web3 economy work with role-based multiple tokens instead of a single token?"

The answer to the first question is the .heylogramtv domain NFT — an identity layer that binds a name to a wallet, like ENS but on Polygon, with a marketplace and openable profile pages.

The answer to the second question is a six-token economy:

  • VOI — task/reward token, mintable with cap
  • HEY — streaming platform token, mintable with cap
  • JVOI — utility token, fixed supply, ownerless (immutable)
  • HLTV — Heylogram TV streaming token, fixed supply, ownerless
  • URA — "Garura's Leaf", earned by clicking, 98.7Q supply cap
  • GARURA — 1 unit in the world, goes to the last clicker
One Sentence
Heylogram TV is a Polygon-based Web3 protocol that gives a wallet anidentity (domain), an activity source (tasks), areward ecosystem (Garura/URA/Badge) and a B2B platform(partner).

1.1 Why Polygon?

A user might complete dozens of tasks weekly, click partner links, and click 8,640 times a day for URA. Ethereum mainnet would be prohibitively expensive for this scale. Polygon brings gas costs down to a level where daily micro-behavior is economical, while remaining fully compatible with QuickSwap, Uniswap V3, OpenSea and similar mature infrastructure.

1.2 Phased Strategy

Deploying 17 contracts at once does nothing but exhaust the ecosystem. Heylogram TV launches functional components as independent families:

  1. Phase 1 (live): Domain NFT + Marketplace + VOI/HEY mint + Tasks + Partner
  2. Phase 2 (live): JVOI + HLTV fixed-supply tokens
  3. Phase 3 (live): URA click game + GARURA grand prize
  4. Phase 4 (shelved): Garura Pool V2 — season system halted on 2026-05-16, contract is still live but no longer called from the frontend
  5. Phase 5 (live): LuckPool V2 (LP NFT random rewards) + custom DEX aggregator (Uniswap V3 + Sushi V3 + QuickSwap V3)
  6. Phase 6 (pending): Liquidity Mining + Badge ERC-1155

02Design Principles

2.1 V1 → V2 Rewrites

Heylogram TV is a protocol already deployed to mainnet — so every bug is met with a new contract version. Summary:

ContractV1 ProblemV2 Solution
GaruraPoolNo point/VOI ratio, partner could withdraw at will, contract didn't know about seasonsFixed 1 VOI = 1000 points; 15% platform / 85% pool auto-split; partner can only withdraw unused
LuckPoolILocker.getLock wrongly defined as 6-return → every register revertedCorrect struct-return ABI + LP NFT content validation (ecosystem token requirement) + emergencyWithdraw
HeylogramDomainFixed price (every domain 6 POL)Tier pricing: 1 letter $10, 2=$5, 3=$2.5, 4=$1.3, 5+=$0.5
HeylogramMarketplaceSingle contract supportParallel old + new domain contract support (_nftKontrat auto-detect)

2.2 Multi-Token Philosophy

In single-token economies, role confusion emerges: "Is this token for payment, reward or governance?" Heylogram TV assigns each role to a separate token:

  • VOI — task/activity reward, seasonal pool fuel
  • HEY — streaming economy, NFT gift payment
  • JVOI — utility (domain purchase, garura participation)
  • HLTV — Heylogram.tv streaming platform
  • URA — gamification (1 URA per click)
  • GARURA — legendary (1 in the world)

2.3 Two Token Types: Mintable vs Fixed

Heylogram TV uses two different token architectures:

  • Mintable + Pausable + Ownable — VOI, HEY, URA. Owner can mint, can pause. Flexible but centralized. Solution: transfer ownership to MintTimelock → mandatory 48h queue.
  • Fixed + Ownerless + Immutable — JVOI, HLTV. All supply minted to deployer at construction; no admin intervention afterwards. CMC's "mintable" warning is historically meaningless for these tokens.
  • Single Unit — GARURA. Only 1 unit, sent to vault (URA click game contract) at deployment.

2.4 Domain-Bound Partnership

The Web3 identity problem: how does a wallet prove who it is? Heylogram TV's answer: .heylogramtv domain NFT.

Becoming a partner requires owning at least one .heylogramtv domain NFT. The moment the domain is sold, partner status is automatically revoked — contracts check ownerOf(tokenId) == partneron every call. This keeps identity and the partner pool bound through the NFT.

2.5 Off-Chain Computation, On-Chain Money

User points, profile data, partner task records, and blog content live in Firestore. Contracts only hold tokens, swap, mint. This hybrid architecture keeps gas costs economical; but data consistency falls to Firestore (see §18 Audit).

2.6 Two-Step Admin Transfer

Modern contracts (GaruraPool V2, LiquidityMining, MintTimelock) use the proposeAdmin + acceptAdmin dual flow. Single-step transfer (no recovery if old admin sets a wrong address) is prevented.

03Architecture Overview

┌─────────────────────────────────────────────────────────────────────┐ │ FRONTEND — web3.jjvoi.com │ │ │ │ Next.js 16 (App Router) + wagmi v2 + RainbowKit v2 │ │ 30+ routes (/dapp, /partner, /pazar, /garura, /ura, /admin, …) │ │ 80+ API endpoints │ └────────────────────────────┬────────────────────────────────────────┘ │ ┌────────────────┴────────────────┐ ▼ ▼ ┌─────────────────────┐ ┌─────────────────────────────┐ │ POLYGON MAINNET │ │ FIREBASE FIRESTORE │ │ │ │ │ │ 16 .sol contracts: │ │ off-chain data: │ │ • 6 tokens │ │ • kullanicilar/{wallet} │ │ • 2 NFT (domain) │ │ • seasons/{seasonId} │ │ • marketplace │ │ • garuraRegistrations/ │ │ • garura v1/v2 │ │ • partners/{domain} │ │ • luck v1/v2 │ │ • partnerGorevler/ │ │ • liq.mining │ │ • domainProfiles/{name} │ │ • mint timelock │ │ • blogPosts/{slug} │ │ • badge (1155) │ │ Server SDK admin access │ │ │ │ (FIREBASE_SERVICE_ACCOUNT)│ └─────────────────────┘ └─────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────┐ │ DEX / NFT MARKETPLACE (mainnet integrations) │ │ │ │ QuickSwap V3 (VOI/POL, HEY/POL liquidity) │ │ Uniswap V3 NonfungiblePositionManager (LP NFT) │ │ OpenSea (HeylogramDomain + HeylagramGaruraBadge collections) │ │ CoinMarketCap DEX listing (VOI) │ └─────────────────────────────────────────────────────────────────────┘

3.1 Contract Families (V2-focused)

FamilyContractsWhat it does
TokenVOIToken, HEYToken, JJVOIToken, HLTVToken, URAToken, GaruraToken6 separate ERC-20s, role-based
IdentityHeylogramDomain (v2), HeylogramMarketplace (v2).heylogramtv domain NFT + trading
Reward PoolGaruraPool v2 shelved, LuckPool v2 activeLuckPool luck-based distribution (Garura season system halted on 2026-05-16)
LiquidityLiquidityMining pendingUniswap V3 LP lock + JVOI reward
GovernanceMintTimelock pending48h mint queue (for CMC warning)
B2BHeylagramGaruraBadge pendingERC-1155 milestone badges (partner-VOI flow in GaruraPool V2)

3.2 Data Flow: A User's Day

USER FIREBASE POLYGON ┌──┐ │👤│ buy domain └──┘ │ └─→ /dapp/domain ─→ HeylogramDomain.mintPOL() ──────────────→ NFT mint │ ▼ /api/mission/domain-mint ──→ Firestore: +points │ seasons/{id} │ earnedDuringSeason+= ┌──┐ │👤│ visit partner site └──┘ │ └─→ partner site → /api/partner/track-visit ──→ Firestore: visit log │ │ (Garura V2 contract) ▼ gorevTamamlandi(partner, points) ──→ 15% admin, 85% pool ┌──┐ │👤│ click URA └──┘ │ └─→ /api/ura/click ─→ rate limit + signature ─→ URAToken.mint(user, 1e18) │ ▼ Firestore: globalClicks++ Check: has 8 billion been reached? Yes → GARURA grand prize End of season: /api/cron/season-distribute ─→ Compute ratios from Firestore ─→ GaruraPoolV2.adminPush(users, amounts) ─→ Firestore: season.status = 'distributed'

3.3 Money Flow

┌──────────────┐ Domain purchase ┌──────────────────────┐ │ User │ ─── POL/VOI/HEY ──▶│ HeylogramDomain │ └──────────────┘ │ (contract owner) │ └──────────┬───────────┘ │ paraCek ▼ Admin wallet ┌──────────────┐ Task completion ┌──────────────────────┐ │ Partner │ ─── deposit VOI ──────────▶ │ GaruraPool V2 │ └──────────────┘ │ │ 15% platform ◀─────────┤ admin balance │ 85% pool ◀─────────┤ pool balance │ └──────────┬───────────┘ │ adminPush ▼ Users ┌──────────────┐ Lock LP NFT ┌──────────────────────┐ │ User │ ─── POL + JVOI ──────────▶ │ LiquidityMining │ └──────────────┘ + 30 days │ │ │ Uniswap V3 LP NFT │ │ rewardPool: 1% JVOI │ └──────────┬───────────┘ │ claim (30d) ▼ LP NFT + 1% JVOI reward ┌──────────────┐ Marketplace sale ┌──────────────────────┐ │ Seller │ ◀── 95% price ────────────│ HeylogramMarketplace │ └──────────────┘ │ │ 5% commission ◀───────┤ │ └──────────────────────┘

3.4 CMC Token Listing Endpoints

CoinMarketCap requires three endpoints for listing a token:total-supply, circulating-supply, andmax-supply. Each returns a plain number (not JSON). Heylogram TV provides these separately for VOI and HEY:

EndpointTokenResponseSource
/api/voi/total-supplyVOIplain text uinton-chain totalSupply()
/api/voi/circulating-supplyVOIplain text uinttotalSupply() − admin wallet − burn
/api/voi/max-supplyVOIplain text uintconstant: 100,000,000
/api/hey/total-supplyHEYplain text uinton-chain
/api/hey/circulating-supplyHEYplain text uinton-chain − admin
/api/hey/max-supplyHEYplain text uintconstant: 2,000,000,000

JVOI, HLTV, GARURA, URA don't have these endpointsbecause they're either not yet CMC-listed or have fixed supply. Added when needed with the same pattern.

Important for CMC
The endpoint must have CORS open (CMC server calls it). Response must return only the number — JSON wrapping, headers etc. cause CMC parse errors.

3.5 Firestore Data Model

Data that's gas-expensive to store on-chain (points, seasons, profiles, partner records) lives in Firestore. Server SDK admin access via the FIREBASE_SERVICE_ACCOUNT_JSON env var on Vercel.

kullanicilar/{wallet}                  // wallet lowercase address
  puan: number                         // total earned points
  epochPuan: number                    // points not yet committed to season
  tamamlananGorevler: string[]         // completed mission ids
  referans: string?                    // referring wallet (optional)
  nickname: string?                    // for URA
  createdAt, lastSeen: Timestamp

seasons/{seasonId}
  token: 'VOI' | 'HEY'
  amount: number                        // tokens in pool
  startTime, endTime: number            // ms timestamp
  status: 'active' | 'scheduled' | 'ended' | 'distributed'
  totalCommitted: number                // total committed points
  totalEarned: number                   // points earned during season
  participantCount: number

garuraRegistrations/{docId}
  wallet: string
  seasonId: string
  committedPoints: number
  earnedDuringSeason: number
  claimed: boolean
  claimedAmount: number

domainProfiles/{name}                  // .heylogramtv name (no extension)
  displayName, bio, avatarUrl, coverColor: string
  links: { twitter, github, telegram, discord, website }
  customText: string

partners/{domainName}                  // doc ID = domain name (NOT wallet)
  wallet: string
  tokenId: number
  activated: boolean                   // is domain still owned?
  logoUrl: string
  website: string
  webhookSecret: string                // for HMAC (W12: plaintext)
  activatedAt: Timestamp

partnerGorevler/{taskId}
  partnerDomain: string
  name, description, url: string
  rewardPoints: number                 // max 500
  totalBudget: number
  spent: number
  status: 'pending' | 'active' | 'ended'
  createdAt: Timestamp

missions/{missionId}                   // platform tasks (admin-managed)
  title, description: string
  type: 'on-chain' | 'profile' | 'social' | 'partner'
  points: number
  repeat: 'once' | 'daily' | 'weekly'
  active: boolean

uraClicks/global                       // single doc, global counter
  total: number                        // total clicks
  lastTick: Timestamp
  milestone8B: { wallet, txHash, timestamp } | null

uraNicknames/{wallet}
  nickname: string
  setAt: Timestamp

uraLeaderboard/{wallet}                // cache, refreshed periodically
  wallet, nickname, totalUra, rank, lastUpdate

votes/{threadId_walletAddr}            // forum/voting spam protection
  threadId, voter, createdAt

blogPosts/{slug}                       // doc ID = URL slug
  title_tr, title_en: string
  slogan_tr, slogan_en: string         // subtitle (optional)
  excerpt_tr, excerpt_en: string       // summary for card
  body_tr, body_en: string             // markdown
  tag: string                          // 'announcement' | 'release' | 'partner' | ...
  coverImage: string                   // optional
  status: 'draft' | 'published'
  publishedAt: number                  // ms timestamp (0 = draft)
  createdAt, updatedAt: number

3.6 Frontend Route Map

Public routes under app/ (hidden admin pages excluded):

RouteFunctionWallet?
/Landing — token grid, recent sales, CTANo
/aboutAbout the project, team, licenseNo
/advertiseShelved (2026-05-16) — former partner B2B promo page
/blogEcosystem announcement list (server-rendered, 30s cache)No
/blog/[slug]Single post, TR/EN content, share buttons, dynamic OGNo
/dapp/dexCustom-built DEX aggregator — 3 venues (Uniswap V3 + Sushi V3 + QuickSwap V3), swap + pool + locker + luck tabsYes
/dapp/domainDomain mint UI — name search, tier pricing, pay POL/VOI/HEYYes
/dapp/uraURA click game — start + wallet connectYes
/garuraShelved (2026-05-16) — former Garura season register + claim
/gorevlerShelved (2026-05-16) — former tasks list
/hesapUser account page — domains, list/cancel, balancesYes
/kesfetDiscovery — newly minted domains, popular profilesNo
/lockerLP NFT lock UI (Locker V1)Yes
/luckLuckPool register + claim UIYes
/partnerShelved (2026-05-16) — former partner panel
/pazarMarketplace — listing, filter, buy (POL/VOI/HEY)Yes (for buying)
/resolve/[name]Domain profile page (Linktree-style)No (owner edits)
/s/[slug]Promo short link — share pagesNo
/tip/[name]Send tip to streamer pageYes (to send)
/tokenVOI/HEY/JVOI/HLTV/URA info page (supply, price, links)No
/trTurkish main landing (i18n)No
/uraURA PWA — tree animation + click + counter (mobile-first)Yes
/ura/leaderboardURA click rankingNo
/whitepaperPoetic main whitepaper (Gaia's tree, TR/EN)No
/whitepaper/technicalOld technical whitepaper (dark theme)No
/whitepaper/uraURA detailed user guideNo
/whitepaper/docsTechnical documentation (Turkish)No
/whitepaper/docs/enThis page — technical documentation (English)No

3.7 Frontend Behavioral Rules

Conventions applied protocol-wide for wallet interactions:

forcePolygon() Pattern

Before every writeContract call, the user must be guaranteed to be on the Polygon network. wagmi'sswitchChain can slip to Ethereum on some wallets; instead, raw provider call is used:

const forcePolygon = async () => {
  await window.ethereum.request({
    method: 'wallet_switchEthereumChain',
    params: [{ chainId: '0x89' }],   // 137 = Polygon
  });
};

// Before every writeContract:
await forcePolygon();
await writeContract({ ... });

Signature + Timestamp Standard

All server-side write operations require EIP-191 signature + 5 minute TTL timestamp. Prevents replay attacks (K06 audit).

Frontend:
  const ts = Math.floor(Date.now() / 1000);
  const msg = "action:<param>:<ts>";
  const sig = await viem.signMessage({ account, message: msg });
  fetch('/api/...', { body: { ...params, timestamp: ts, signature: sig } });

Server:
  if (now - ts > 5*60) return 401;             // 5min TTL
  const recovered = ecrecover(msg, sig);
  if (recovered !== wallet) return 401;

Wallet Address Normalization

All wallets in Firestore are stored lowercase. APIs applywallet.toLowerCase(). The frontend address fromuseAccount may be checksummed — normalized before comparison.

RPC Selection

  • https://polygon.drpc.org
  • https://1rpc.io/matic
  • polygon-rpc.com — broken, don't use
  • rpc.ankr.com/polygon — broken, don't use

RainbowKit Wallet Order

injectedWallet must be first — so MetaMask opens directly. Otherwise RainbowKit shows a selection screen and UX degrades.

3.8 DEX Aggregator (/dapp/dex)

Heylogram TV has a custom-built multi-venue DEX aggregator (lib/dexTokens.ts + lib/dexAdapters.ts). A single frontend queries three Polygon DEXs in parallel, picks the best price, and performs swap or LP positions accordingly. The /dapp/dex page has four tabs:Swap, Pool,Locker, Luck.

Supported Venues

VenueProtocolQuoterRouterPosition Manager
Uniswap V3univ3 (4 fee tiers)0x61fF…B21e0x68b3…Fc450xC364…FE88
Sushi V3univ3 (fork, same ABI)0x64e8…08A70x0aF8…44c30xB740…a40
QuickSwap V3algebra (dynamic fee)0xa15F…FC890xf5b5…8E120x8eF8…1de6

Uniswap V3 and Sushi V3 share the same ABI (4 fixed fee tiers:100 / 500 / 3000 / 10000). QuickSwap V3 uses Algebra Integral — no fee parameter, the pool computes fee dynamically.

Token Catalog

  • Stable + Native: POL (native MATIC, wrap: WMATIC), USDC (6 dec), USDT (6 dec)
  • Ecosystem: URA, HEY, VOI, JVOI, HLTV, GARURA (all 18 dec)
  • Total 9 tokens; POLYGON_TOKENS constant object

URA / Stable Liquidity Map

URA/USDC   → Sushi V3 0.3%   (main pool)
URA/POL    → Uniswap V3 1%
HEY/POL    → Uniswap V3 0.3%
VOI/POL    → Uniswap V3 0.3%
JVOI/POL   → Uniswap V3 0.3%
HLTV/POL   → Uniswap V3 0.3%
USDC/USDT  → Stable 0.01%

Aggregator Flow

User: 1 URA → ? USDC
   │
   ▼
quoteBestVenue(tokenIn=URA, tokenOut=USDC, amountIn=1e18)
   │
   ├─→ quoteFromVenue(uniswap)   → try all fee tiers + pool-based fallback
   ├─→ quoteFromVenue(sushi)     → pool-based (Sushi's Quoter is broken)
   └─→ quoteFromVenue(quickswap) → Algebra quoter (dynamic fee)
   │
   ▼  Promise.all parallel
   │
   ▼
Compare results → venue with max amountOut wins
   │
   ▼
buildSwapTx(venue, ...) → router-specific tx (univ3 vs algebra ABI)
   │
   ▼
walletClient.writeContract(...)

Pool-Based Fallback Quote

When the Quoter contract isn't working (like Sushi V3) or certified, quoteFromPoolBased kicks in: get the pool address from factory, read slot0.sqrtPriceX96, compute via a simple price formula:

price (token1/token0) = (sqrtPriceX96)² / 2²⁰⁰

amountOut (token0 → token1) = amountIn × price
amountOut (token1 → token0) = amountIn / price
amountOut -= amountOut × fee / 1_000_000   // 0.3% = 3000

This fallback is accurate for small swaps; for large swaps it doesn't account for slippage — just an estimate. For real swaps the router is still called with amountOutMinimum(user sets slippage tolerance).

LP Mint (Pool Tab)

buildMintTx creates a new LP position. Uniswap V3 / Sushi V3 require a fee parameter; Algebra doesn't. For full-range positions, ticks are computed viafullRangeTicks(spacing):

spacing  fee
  1       100      (stable)
  10      500
  60      3000     (common)
  200     10000    (volatile)
  60      algebra  (Quickswap, default)

tickLower = ceil(-887272 / spacing) × spacing
tickUpper = floor(887272 / spacing) × spacing

Cross-DEX Reference Price

When opening a new pool (token listed on one DEX but not another), findReferencePrice searches all venues in parallel; returns the sqrtPriceX96 of the first pool it finds along with the real display price (accounting for token0 + token1 decimals). Used to set the correct starting price when creating a new pool.

Locker + Luck Tabs

  • Locker: LockerPanel component (29KB) — user locks their LP NFT into the Locker V1 contract (0x3df6…CA98). Duration selection, lock confirmation, existing locks list.
  • Luck: LuckPanel component (11.5KB) — registers the user's locked LP NFT into LuckPool V2 (register(lockId)), claims the reward when time is up.

These two tabs combine the DEX → LP → Lock → Luck flow on asingle screen. After a swap, the user can create an LP position, lock it, and register for Luck from the same page.

Native POL Swap

For swaps with native POL, WMATIC is wrapped (POL address = 0x0, so the adapter uses thewrapped field). The native value is passed to the router via the nativeValue parameter.

04Token Family

TokenTypeMax SupplyMintPauseOwner
VOIMint complete100,000,000Cap reachedYes (unused)Owner
HEYMint complete2,000,000,000Cap reachedYes (unused)Owner
JVOIFixed777,777,777NoneNoneOwnerless
HLTVFixed629,629,629NoneNoneOwnerless
URAMintable (lockable)98,774,312,000,000,000Minter (lockable)NoneOwner (renounceable)
GARURASingle unit1NoneNoneOwnerless

4.1 VOI — Task/Activity Token

ERC20 + ERC20Permit + ERC20Burnable + Pausable + Ownable. 100M cap. The contract remains mintable on paper, butthe full 100M cap has been minted — there is no operational minting anymore, and the admin panel mint UI has been removed. EIP-2612 permit allows approve via signature; no transfer tax.

Use: DEX liquidity pools (Uniswap V3 / Sushi V3 / QuickSwap V3), marketplace payments for domain purchases.

4.2 HEY — Streaming Token

Same structure as VOI, different supply (2B). The full 2B cap has been minted — operational mint is closed, admin UI was removed. Main reward token of the Heylogram TV streaming platform — currently used for domain and marketplace payments; ready for the future live streaming platform.

Mint Operation Complete
Both VOI and HEY caps (100M + 2B) have been fully minted. Themint function on the contracts is technically still callable because they inherit OZ Ownable, but no address requests new mints; the admin panel mint button was removed (2026-05-16). The permanent solution for CMC's "Mintable" warning would be to call renounceOwnership() — currently a pending decision (ownerless vs future flexibility).

4.3 JVOI — Ownerless Utility Token

Completely different architecture. Does not use OpenZeppelin — raw ERC-20. No Ownable. No mint. NoPausable. The constructor mints all supply (777,777,777 JVOI) to the deployer; then the contract is immutable forever.

On-chain metadata: website,whitepaper, platform, descriptionstored as constant strings — visible in the explorer.

Why 777,777,777?
A symbolic number chosen by the user. A luck + abundance metaphor.

4.4 HLTV — Streaming Platform Token

Same architecture as JVOI (raw ERC-20, ownerless, immutable). Supply 629,629,629 — symbolic amount equal to the hex digit sum of two of the user's vanity deployer addresses.

Target use: Heylogram.tv (future live streaming platform) — streamer tipping + NFT gift economy.

4.5 URA — Garura's Leaf

Click game token. Design decision: DEX safetyachieved by removing all "suspicious" features:

  • Pause none
  • Blacklist none
  • Transfer tax none
  • Upgrade none
  • Mint from a single address (click game contract)
  • Minter locked forever via lockMinter()
  • After lock, renounceOwner() → fully ownerless

Total supply: 98,774,312,000,000,000 URA. Takes about ~12,350 clicks per global population to deplete.

4.6 GARURA — 1 in the World

Total supply 1 (= 1 × 10¹⁸ wei). No mint function. No pause. No owner. Sent to the URA click game contract (vault) at deployment.

Earning: the wallet that performs the 8,000,000,000th click takes GARURA from the vault. _updateis overridden to write every transfer as a GaruraElDegistirdievent on-chain — a historical record.

05Domain System

HeylogramDomain is an ERC-721 — representing.heylogramtv names as NFTs. Old + new contracts operate in parallel; the new contract uses tier pricing.

5.1 Tier Pricing

LengthPOLVOIUSD (~POL ~$0.084)
1 letter1201,500~$10
2 letters60750~$5
3 letters30400~$2.5
4 letters15200~$1.3
5+ letters680~$0.5

5.2 Name Rules

  • 1–63 characters
  • Only lowercase (a–z), digits (0–9), hyphen (-)
  • .heylogramtv suffix is auto-stripped

5.3 Three Currencies

  • mintPOL(name, ipfsLink) — native POL
  • mintVOI(name, ipfsLink) — VOI (transferFrom)
  • mintHEY(name, ipfsLink) — HEY (fixed price)
  • mintUcretsiz(to, name, ipfsLink) — onlyOwner; for airdrop / team

5.4 Profile Page

Every domain has a Linktree-style profile at/resolve/[name]. Profile data in Firestore (domainProfiles/{name}):

displayName, bio, avatarUrl, coverColor
links: { twitter, github, telegram, discord, website }
customText

The owner can connect their wallet and edit their profile. When ownership changes, the profile auto-transfers to the new owner (Firestore wallet field syncs with ownerOf(tokenId)).

5.5 Old + New Parallel

Old contract (0xc654…0816) is the first version, fixed price. New contract (0xc993…A826) is tier-priced V2. Both are active; marketplace and partner contracts auto-detect via_nftKontrat().

Token ID Collision
Both contracts have tokens with IDs #1, #2 etc. — the same ID can be assigned to two different domains. The UI disambiguates with an ?old=1 query parameter. Accepted known behavior.

06Marketplace

HeylogramMarketplace v2 is the domain NFT trading layer. Automatically supports both old + new domain contracts.

6.1 Three Currencies

Seller chooses one of the ParaBirimi enum:POL, VOI, HEY. Buyer callspolIleSatinAl, voiIleSatinAl orheyIleSatinAl with the same currency.

6.2 Commission

  • komisyonBaz = 500 (= 5%)
  • Maximum komisyonGuncelle: owner can adjust up to 10%
  • Commission to komisyonAlici; remainder to seller

6.3 Flow

1. Seller approves the domain NFT
2. listele(tokenId, fiyat, paraBirimi)
3. Added to aktifListeIds[]
4. Buyer calls the buy function
5. Commission → komisyonAlici
6. Net price → seller
7. NFT → buyer
8. listeyiIptalEt or auto (after sale)

6.4 Old Contract Detection

function _nftKontrat(tokenId) returns (IERC721, bool legacy) {
    try domainNFT.ownerOf(tokenId) returns (address o) {
        if (o != 0) return (domainNFT, false);
    } catch {}
    return (domainNFTLegacy, true);
}

If a token doesn't exist in the new contract, it falls back to the old. This determines which NFT contract should hold the listing.

07Garura System SHELVED · 2026-05-16

This System Is No Longer Used
The Garura season mechanism was shelved on 2026-05-16. The/garura, /gorevler and /advertisepages were disabled with ShelvedNotice (original code is preserved in archived-page.txt files). The GaruraPool V2 contract remains live on mainnet (0x8755…DFBb0) but is no longer called from the frontend; partners can still recover their unused balance via partnerCek(). The season system was halted due to economic complexity — a simpler model based on VOI/HEY liquidity + URA click game was adopted.

This section is preserved as documentation for history + system behavior. If reopened, it would be planned with a V3 contract + Chainlink VRF integration.

The following describes the old system (during its live period):

Garura is the seasonal reward pool. Partners deposit VOI, users complete tasks, and the pool was distributed proportionally at season end. Two versions exist; V2 was in active use.

7.1 V2 — 1 VOI = 1000 Points (fixed rate)

V1's biggest issue: partners could withdraw funds at any time, and there was no point/VOI ratio. V2 solves this with a fixed economic model:

PUAN_VOI_ORANI = 1000     // 1 VOI = 1000 points (never changes)
PLATFORM_PAYI  = 15       // 15% admin, 85% pool

partnerYatir(voiAmount, tokenId, isOld):
   - Check domain ownership
   - Pull VOI, partner credit = voiAmount × 1000 / 1e18 points

gorevTamamlandi(partnerWallet, pointAmount):  // backend calls
   - Enough point credit?
   - VOI equivalent = pointAmount × 1e18 / 1000
   - 15% → adminBakiyesi
   - 85% → havuzBakiyesi
   - partner.kullanilanPuan += pointAmount
   - partner.kilitliVOI    += poolPortion
   - partner.platformVOI   += adminPortion

partnerCek(voiAmount):
   - Only "free balance" = toplamYatirilan - kilitliVOI - platformVOI
   - i.e., locked portion can't be withdrawn

adminPush(users[], amounts[]):    // end of season
   - Only spent from havuzBakiyesi
   - Max 500 users/tx

7.2 Operator System

gorevTamamlandi() is called by the backend server. To avoid admin being the sole authority, there's anoperators mapping — admin can add new operators (e.g., a second server), but operators cannot withdraw funds.

7.3 Season Computation (off-chain)

The contract knows nothing about seasons. On the Firestore side:

seasons/{seasonId} {
  token:           'VOI' | 'HEY',
  amount:          number,        // tokens in pool
  startTime, endTime,
  status:          'active' | 'scheduled' | 'ended',
  totalCommitted:  number,        // total committed points
  totalEarned:     number,
  participantCount: number
}

garuraRegistrations/{docId} {
  wallet,
  seasonId,
  committedPoints,
  earnedDuringSeason,       // extra points earned during season
  claimed: boolean
}

At season end, /api/cron/season-distribute computes ratios from Firestore and writes to the contract viaadminPush().

7.4 Share Computation

user_share = (committedPoints + earnedDuringSeason)
              / total_pool_points × season_amount

The 15/85 split is handled in-contract at deposit. Note:The earlier app/api/garura/claim/route.ts additionally deducted 10% on top — this is a known issue (see §18 N6); the POST endpoint was fixed but the GET endpoint still shows the old deduction.

7.5 Emergency / Management

  • setPaused(true) — temporary halt
  • deprecate() — permanent close (for migration)
  • emergencyWithdraw(to, amount) — only when paused/deprecated
  • proposeAdmin / acceptAdmin — 2-step admin transfer

08Task System SHELVED · 2026-05-16

UI Closed, APIs Still Live
The /gorevler page was disabled withShelvedNotice on 2026-05-16. The system's main anchor was the Garura season — once Garura was shelved, completing tasks lost its economic purpose. The/api/mission/* endpoints were not deleted (some may still be used for URA milestone and domain mint points), but the end-to-end flow (task → points → season → reward) is now closed.

The description below preserves the old system as documentation.

On Heylogram TV, completing tasks earned points. Points were stored in Firestore and converted to VOI/HEY from the Garura pool at season end.

8.1 Task Types

TypeVerificationStatus
On-chain (buy / list / sell domain)Event log + tx hash verifylive
Complete profileFirestore profile fieldslive
Partner site visitScript + wallet paramlive
X (Twitter) shareManual approval / honor systemtemporary
Referral?ref=WALLET cookie + txplanned

8.2 Signed Completion Flow

1. User performs the task (e.g., visits a partner site)
2. Frontend: signs a message via viem
   message = "mission:<taskId>:<timestamp>"
3. POST /api/mission/complete { wallet, taskId, signature, timestamp }
4. Server:
   - Timestamp within 5min?
   - Wallet regex correct?
   - Signature ECDSA recover correct?
   - Was this mission already completed?
5. Firestore: kullanicilar/<wallet> -> puan += task.points
   if registered in active season: earnedDuringSeason += task.points

8.3 Tracker Script

A partner embeds <script src="https://web3.jjvoi.com/api/track/script/<siteKey>?hw=<wallet>"></script> on their site. The script reports the visit to/api/track/visit; if the user has a wallet, Firestore points increase.

Bot protection: the same IP+wallet is counted at most once in 24 hours. Rate limiting is still under development (see §18 W04).

09Partner / B2B SHELVED · 2026-05-16

Frontend Shelved, Contract/API Live
The /partner page was disabled on 2026-05-16 withShelvedNotice. The partner system was tied to the Garura pool — once Garura was shelved, the economic loop where partners deposit VOI collapsed. The /api/partner/*endpoints and the GaruraPool V2 contract still live on mainnet, but new partner activations are no longer accepted.

The description below preserves the old design as documentation. It may be reopened with a different B2B model (e.g., direct deposits to DEX liquidity pools + Badge mint).

It was Heylogram TV's most distinctive feature:partner sites that create their own tasks. The site owner came in, defined tasks, redirected our users to their site, and deposited VOI to the reward pool.

9.1 Partner Eligibility

  • Must own at least 1 .heylogramtv domain NFT
  • "Become Partner" button on the /resolve/[name] profile page
  • If the domain is sold, partner status is auto-revoked (lazy deactivation)

9.2 Activation Flow

POST /api/partner/activate
{ wallet, domainName, timestamp, signature }

Server:
  1. Timestamp within 5min?
  2. Verify signature via ECDSA
  3. For domainName, ownerOf(tokenId) === wallet (on-chain)
  4. Firestore: partners/{domain} = { wallet, tokenId, activated: true, ... }

9.3 Task Creation

  • "Create Task" button on the partner panel
  • Site name, URL, reward amount (points), conditions
  • Deposit VOI to GaruraPool V2 (receives point credit)
  • Downloads custom JS or Kotlin integration code

9.4 What Happens When Domain is Sold?

  1. UI: /api/partner GET does a live ownerOf(tokenId) check on every request; if old owner, activated: false
  2. Contract: partnerCek() on GaruraPool V2 only returns unused VOI; the portion already locked to the pool remains
  3. Firestore: partners/{domain}.activated field is set to false, partner panel won't open

9.5 Webhook System

When the partner site notifies of a visit, HMAC signature is used:

POST /api/partner/webhook
{ taskId, userWallet, timestamp, signature }

verifyHmac(webhookSecret, taskId, userWallet, timestamp, signature)

webhookSecret is in Firestore; flagged asW12 in the audit — in production, Security Rules should restrict it to admin only (see §18).

10HeylagramGaruraBadge (ERC-1155)

Partner milestone NFT system. A single ERC-1155 contract; partners open their own level collections. Appears on OpenSea as one collection: Heylagram-Garura.

10.1 Two-Layer Structure

  • Partner Collection — 1 collection per partner wallet. The name, logo, and description are set by the partner.
  • Level — each collection can have as many levels as desired. Each level gets a random tokenId.

10.2 Random TokenId Generation

No sequential 1, 2, 3 collision issue like domain NFTs. For each level:

tokenId = keccak256(
    "heylagram-garura-badge",
    partnerId,
    levelIndex,
    block.timestamp,
    block.prevrandao,
    msg.sender,
    partnerCount
)

In case of collision, tokenIdExists[candidate] is checked; if it collides, keccak256(candidate, block.timestamp)is recomputed. In practice, never collides.

10.3 Fees

ActionUSDTUSDCVOIHEY
Create collection10101,00010,000
Add level222002,000
Mint commission5% (max 20%, admin-adjustable)

10.4 Mint Paths

  • adminMint(partnerId, levelIndex, user) — called by backend on milestone
  • adminMintBatch — end-of-season batch mint (max 200 users)
  • userMint(partnerId, levelIndex) — user pays their own fee

10.5 contractURI and OpenSea

contractURI() pure returns inline JSON:
  name: "Heylagram-Garura"
  description: "Heylogram TV partner badge collection..."
  external_link: "https://web3.jjvoi.com"

Each tokenId's metadata URI is set by the partner (IPFS / HTTPS — PNG, GIF, animated SVG, WebP).

11URA & GARURA

Click game mechanism: connect wallet, click, earn 1 URA. Total supply 98.7 quadrillion — practically very long to deplete. As it depletes, the wallet that reaches the8 billion click threshold wins GARURA.

11.1 Flow

POST /api/ura/click
{ wallet, timestamp, signature }

Server:
  1. Wallet regex check
  2. Rate limit: 1 click per 10 seconds
  3. Signature ECDSA verify
  4. URAToken.mint(wallet, 1e18)
  5. Firestore globalClicks++
  6. Milestone check:
     if (globalClicks === 8_000_000_000):
       grandPrize: URA milestone tx hash, date, wallet recorded
       (GARURA transfer vault → wallet, possibly automatic)

11.2 Leak Protections

  • No pause in token contract → DEXs cannot freeze
  • No blacklist in token contract → no address can be blocked
  • No transfer tax → fully DEX-compatible
  • Minter set once, then locked via lockMinter()
  • renounceOwner() → contract becomes fully ownerless

11.3 PWA Frontend

The /ura page is mobile-first, can be added to home screen. Tree animation + click + global counter. Hidden access via QR (no direct link on web).

11.4 GARURA Historical Record

Each transfer is written to blockchain as GaruraElDegistirdi(from, to, timestamp, blockNumber) event — staying in history forever.

11.5 Nickname & Leaderboard

Firestore-based nickname system to show alias instead of wallet address:

  • POST /api/ura/nickname — signed, 3–20 chars, unique
  • GET /api/ura/leaderboard?limit=100 — periodic snapshot, sorted by on-chain balance
  • Leaderboard cache in uraLeaderboard/ collection; cron refreshes every 15min
  • Top 100 always cached; deeper queries computed on-demand

11.6 Referral System

If new URA-clicking users arrive via a referral link, both inviter and invitee earn extra URA:

Link: https://web3.jjvoi.com/ura?ref=<wallet>

First-click flow:
  /api/ura/click { wallet, ref?, timestamp, signature }
  → /api/ura/referral { wallet, referrer }
    → Firestore: kullanicilar/{wallet}.referans = referrer (one-time)
    → Bonus URA: +10 to inviter, +5 to invitee
    → Cooldown: same referrer counts max 20 invites in 24h (bot protection)

11.7 In-App Wallet & Session

Because URA runs as a mobile PWA, some users may not have MetaMask. So the in-app wallet option:

  • Anonymous session key for wallet-less users (browser localStorage)
  • POST /api/ura/session — signed, starts session
  • User points/URA accumulate in Firestore; when wanted, can be linked to a real wallet and minted on-chain
  • /api/ura/batch-mint — mints accumulated points to contract in a single tx (gas-efficient)
UX Decision
Wallet-less users start the game instantly, connect an account later. Minimum Web3 onboarding friction.

11.8 Detailed User Guide

A separate long guide exists for URA's user-facing side:/whitepaper/ura. That page contains step-by-step clicking, PWA install, leaderboard entry, milestone race, grand prize rules and other end-user content.

12Liquidity Mining

LiquidityMining.sol — user deposits POL + JVOI, a Uniswap V3 LP NFT is created, locked for 30 days, and at the end the user receives the LP NFT + 1% JVOI reward.

12.1 Two Position Types

TypeLockMin DepositJVOI Reward
STANDARD30 days0 POL1%
ANGEL (investor)180 days100 POL5%

12.2 Flow

  1. deposit(jvoiAmount) payable — user sends POL + JVOI
  2. Contract increments lockedUserJvoi counter
  3. Backend creates Uniswap V3 LP within 48 hours
  4. Backend calls lockPosition(positionId, nftTokenId)
  5. When time is up, user calls claim(positionId) to receive LP NFT + JVOI reward

12.3 Refund Mechanism

If the backend fails to link an NFT within 48 hours, the user can call refundPosition(positionId) to recover POL + JVOI. Constant: REFUND_TIMEOUT = 48 hours.

12.4 emergencyWithdrawToken Hardening

function emergencyWithdrawToken(token, amount):
    if (token == JVOI):
        protected = rewardPool + lockedUserJvoi
        require(balance >= protected)
        withdrawable = balance - protected
        require(amount <= withdrawable)
    transfer(admin, amount)

Admin can only withdraw JVOI that is outside user funds and reward pool. Other ERC-20s mistakenly sent can be fully withdrawn.

13LuckPool V2

A lottery pool targeted at LP NFTs locked in the existing Locker contract (0x3df6…CA98). Lock is registered; when time is up, the user wins a random ecosystem token.

13.1 V1 → V2 Bug Fix

V1 Bug
ILocker.getLock was defined as 6 separate return parameters. Actually, the contract returns a Lockstruct. Due to the ABI mismatch, every register call reverted. V1 is functionally non-working.
V2 Fix
The struct Lock definition andILocker.getLock(...) returns (Lock memory) were defined with the correct ABI. Additionally:
  • LP NFT content check (Uniswap V3 NPM only)
  • Ecosystem token requirement (at least one of VOI/JVOI/HEY/HLTV/URA/GARURA)
  • Liquidity > 0 check
  • emergencyWithdrawPrize — admin can emergency-withdraw tokens

13.2 Registration Flow

register(lockId):
  Lock l = ILocker.getLock(lockId)
  require(l.owner == msg.sender)
  require(!l.withdrawn)
  require(l.unlockTime > now)             // still locked
  require(l.nftContract == NPM)            // Uniswap V3 only
  positions(l.tokenId) → t0, t1, liquidity
  require(liquidity > 0)
  require(ecosystemTokens[t0] || ecosystemTokens[t1])
  entries[++entryCount] = Entry(...)

13.3 Random Selection

rand1 = keccak256(
    block.prevrandao, block.timestamp, entryId,
    unlockTime, msg.sender, block.coinbase
)
token = available[rand1 % count]
maxAmt = poolRemaining / 20  // max 5% of pool
amount = (rand2 % maxAmt) + 1e18
Random Safety
On-chain randomness is vulnerable to validator manipulation. Polygon validators may know prevrandao in advance. Acceptable for low-value rewards; for high-value lotteries, Chainlink VRF should be used.

14MintTimelock CANCELLED · 2026-05-16

This Contract Won't Be Deployed
The plan was cancelled — VOI/HEY caps are fully minted, so the mint operation is effectively closed (see §4.1–4.2). CMC no longer shows the "Mintable" warning; only Contract Not Renounced + Pause + Low Liquidity remain (see §19.2). The contract source lives inpolygon.heylogramtv/contracts/MintTimelock.sol but will not ship to mainnet.

The description below is preserved as a design document.

It was written to soften CoinMarketCap's "Mintable" security warning. Token owners would be transferred to this contract; every mint would wait 48 hours in queue.

14.1 Flow

1. token.transferOwnership(MintTimelock.address)
2. Admin: queueMint(token, to, amount) → id
3. 48-hour countdown begins (DELAY = 48h)
4. When time is up, anyone can call: executeMint(id)
   → token.mint(to, amount)
5. If admin spots a dangerous mint: cancelMint(id)
6. Queue older than 14 days expires (GRACE = 14d)

14.2 Admin Two-Step

proposeAdmin(newAdmin)acceptAdmin(). Prevents accidental transfer to an unauthorized address.

14.3 Token Ownership Transfer

transferTokenOwnership(token, newOwner) — onlyAdmin
  // Transfer token ownership elsewhere (for emergency)
  // This call is NOT TIMELOCKED
New Finding (during writing)
transferTokenOwnership is not 48h timelocked. If admin key is stolen, an attacker can transfer token ownership to another address in a single tx and mint there.Solution: tie this function to the queue/execute flow as well, or protect with a multi-sig. See §19.

15Contract Reference

16 .sol files — 15 production + 1 mock. For each contract: one-line role, inheritance, critical functions, and behaviors.

VOIToken.sol100M cap, mintable, pausable, ERC20Permit
Inherits
ERC20Permit, ERC20Burnable, Ownable, Pausable
Constructor
(address initialOwner)
Max Supply
100,000,000 × 10¹⁸

Functions

  • mint(to, amount) — onlyOwner; cap check
  • batchMint(recipients[], amounts[]) — onlyOwner; for airdrops
  • pause / unpause — onlyOwner
  • burn / burnFrom — standard ERC20Burnable
  • permit — EIP-2612 signature-based approve
HEYToken.sol2B cap — same structure as VOI

Same inheritance, functions, behaviors as VOI; only difference is MAX_SUPPLY = 2,000,000,000 × 10¹⁸.

JJVOIToken.sol777M fixed · ownerless · immutable
Inherits
None — raw ERC-20
Constructor
no parameters · entire supply minted to deployer
Supply
777,777,777 × 10¹⁸ (symbolic)

Features

  • No owner. Ownable not inherited.
  • No mint. No mint function.
  • No pause. Pausable not inherited.
  • No blacklist. No address can be blocked.
  • On-chain metadata: website, whitepaper, platform, description constants
  • unchecked blocks in transfer — gas optimization
HLTVToken.sol629M fixed · ownerless · streaming token

Same architecture as JVOI, different supply (629,629,629). Main token of the (future) Heylogram.tv live streaming platform.

URAToken.sol98.7Q cap · lockable minter · DEX-safe
Inherits
ERC20, ERC20Permit, ERC20Burnable
Constructor
(address deployer)
Max Supply
98,774,312,000,000,000 × 10¹⁸
Minter
Single address (click game contract), lockable via lockMinter()

Functions

  • setMinter(address) — onlyOwner, set once before lock
  • lockMinter() — lock forever
  • renounceOwner() — fully relinquish ownership
  • mint(to, amount) — onlyMinter
GaruraToken.sol1 unit · ownerless · deployed to vault
Inherits
ERC20, ERC20Permit, ERC20Burnable
Constructor
(address vault) — entire supply minted to vault
Supply
1 × 10¹⁸ (total 1 GARURA)

Features

  • No mint function — minted only in constructor
  • No pause, no blacklist, no owner
  • Every transfer emits GaruraElDegistirdi event
HeylogramDomain.sol.heylogramtv NFT · tier price · POL/VOI/HEY
Inherits
ERC721, Ownable, ReentrancyGuard
Constructor
(initialOwner, voiToken, heyToken, polUcreti, voiUcreti, heyUcreti)

Functions

  • fiyatHesapla(name) — pure; POL+VOI by name length
  • mintPOL / mintVOI / mintHEY — user pays to mint
  • mintUcretsiz(to, name, ipfsLink) — onlyOwner; airdrop
  • ipfsGuncelle(tokenId, link) — owner only
  • domainSorgula(name) — view; (taken, owner, ipfs, date)
  • rezerveEt(name) — onlyOwner
  • paraCek — onlyOwner; POL + VOI + HEY balances
HeylogramMarketplace.solv2 · old + new NFT support · 3 currencies
Inherits
Ownable, ReentrancyGuard
Constructor
(domainNFT, domainNFTLegacy, voi, hey, komisyonAlici)
Commission
komisyonBaz = 500 (=5%), max 10%

Functions

  • listele(tokenId, price, currency)
  • listeyiIptalEt(tokenId)
  • polIleSatinAl / voiIleSatinAl / heyIleSatinAl
  • aktifListeler / listeDetay
  • komisyonGuncelle / komisyonAlicisiGuncelle — onlyOwner
GaruraPool (v1 + v2)v1: deprecated · v2: 1 VOI = 1000 points fixed

V1 (unused): Token whitelist, ECDSA-signed claim, separate partner pool, admin push, deprecate mechanism. Missing: no fixed rate, partner can withdraw at will.

V2 (active):

Inherits
ReentrancyGuard
Constants
PUAN_VOI_ORANI = 1000, PLATFORM_PAYI = 15
  • partnerYatir(voiAmount, tokenId, isOld)
  • gorevTamamlandi(partnerWallet, pointAmount) — onlyOperator
  • partnerCek(voiAmount) — free balance only
  • adminPush(users[], amounts[]) — onlyAdmin, max 500
  • adminCek / adminCekTumu — platform share
  • setPaused / deprecate / emergencyWithdraw
  • setOperator / proposeAdmin / acceptAdmin
LuckPool (v1 + v2)v1: ABI bug · v2: fixed

V1: ILocker interface wrongly defined as 6 returns — every register reverts. Unusable.

V2 (active):

  • Correct struct Lock ABI
  • Uniswap V3 NPM LP NFT only
  • 6 ecosystem token whitelist (VOI/JVOI/HEY/HLTV/URA/GARURA)
  • addPrize / emergencyWithdrawPrize — onlyAdmin
  • register(lockId) — 7 checks
  • claim(entryId) — random token + max 5%/pool
LiquidityMining.solPOL + JVOI → Uniswap V3 LP + reward
Constants
JVOI (hardcoded), WMATIC, NPM, FEE_TIER = 3000, LOCK_DAYS = 30, ANGEL_LOCK_DAYS = 180, REWARD_BPS = 100, ANGEL_REWARD_BPS = 500, REFUND_TIMEOUT = 48h
  • fundRewardPool(amount) — onlyAdmin, deposits JVOI
  • deposit(jvoiAmount) payable — POL + JVOI
  • lockPosition(positionId, nftTokenId) — onlyAdmin, backend binds
  • angelDeposit — 100 POL min, 180 days, 5% reward
  • refundPosition — if backend doesn't bind NFT within 48h
  • claim — LP NFT + JVOI reward
  • emergencyWithdrawToken — for JVOI, can't touch protected portion
MintTimelock.sol48h queue · CMC Mintable warning solution
Constants
DELAY = 48h, GRACE = 14d
  • queueMint(token, to, amount) — onlyAdmin
  • executeMint(id) — anyone (after 48h)
  • cancelMint(id) — onlyAdmin
  • transferTokenOwnership(token, newOwner) — onlyAdmin, not timelocked
  • proposeAdmin / acceptAdmin
HeylagramGaruraBadge.solERC-1155 · random tokenId · 4 payment tokens
Inherits
ERC1155, Ownable, ReentrancyGuard
Constructor
(voi, hey, usdt, usdc)
  • createCollection — partner; fee in one of 4 tokens
  • addLevel — partner; random tokenId generated
  • updateLevel / updateCollection
  • adminMint / adminMintBatch — onlyOwner, milestone
  • userMint — if partner set a fee
  • setCreationFee / setLevelFee / setMintCommission — onlyOwner
  • contractURI — OpenSea inline JSON

16Live Contract Addresses

Polygon Mainnet. Sources verified on PolygonScan.

ContractAddressStatus
VOI Token0xc8Ad9EB7D26337E598AFE89bF69d21455BD10501live · CMC-listed
HEY Token0xC9e3488E3CDAA944fDBFce0DEf602d4b09db4032live
JVOI Token0xa50eBF45c9C8D7F45C861D6064165E910e3fbEc5live · ownerless
HLTV Token0x8C55F7815b619e07B16fed6C3D0418684e441038live · ownerless
URA Token0xaafbd87e2D69Dc317276a16E2c195d269214b7C5live
GARURA Token0x427E0a0F5B8b70e2f98CcA860FdFF58055C253C5live · 1 unit
Domain NFT (new)0xc993f5A85724a609A4747e9c237A12C57F13A826live · v2
Domain NFT (old)0xc654129EF0FE6677d7A01B852D1113a0110b0816old · parallel
Marketplace (new)0x88Be8507cB3750e16331Fe10601fB0297d4C35ealive · v2
Marketplace (old)0x3C7a64bCc80DF0726B4Ddfae2621ccB0Ea3fD3Ceold
GaruraPool V10xf1Ba8A40b89B6D7EdE52159A06F867508B7C731adeprecated
GaruraPool V20x87555b791A30ec5B10150ac1BD399741030DFBb0live
MintTimelockpending deploy
LiquidityMiningpending deploy
LuckPool V20xd20e8ABDd5b767D6E112F0f51B058BDa0715B9Cflive
HeylagramGaruraBadgepending deploy

16.1 Management Addresses

  • Admin wallet: 0x425490412f1F6e4c6DB58DE0134548dF4Ec22c70
  • Uniswap V3 NPM (Polygon): 0xC36442b4a4522E871399CD717aBDD847Ab11FE88
  • WMATIC: 0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270
  • Locker V1: 0x3df61b76622eC494cEE2f586914DB14913c5CA98
  • USDT (Polygon): 0xc2132D05D31c914a87C6611C10748AEb04B58e8F
  • USDC (Polygon): 0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359

16.2 RPC Usage

  • https://polygon.drpc.org
  • https://1rpc.io/matic
  • polygon-rpc.com — broken
  • rpc.ankr.com/polygon — broken

16.3 API Endpoint Catalog

There are 60+ endpoints under app/api/. Grouped table — hidden admin endpoints are not listed.

Token & Supply (CMC + General)

EndpointMethodResponseDescription
/api/voi/total-supplyGETplain uintFor CMC
/api/voi/circulating-supplyGETplain uintFor CMC
/api/voi/max-supplyGETplain uintFor CMC (constant 100M)
/api/voi-priceGETJSONVOI price oracle (from DEX)
/api/hey/total-supplyGETplain uintFor CMC
/api/hey/circulating-supplyGETplain uintFor CMC
/api/hey/max-supplyGETplain uintFor CMC (constant 2B)
/api/token-infoGETJSONMetadata summary for all tokens
/api/wallet/tokens?wallet=GETJSONAll ecosystem tokens in a wallet

Domain & Resolve

EndpointMethodDescription
/api/resolve?name=GETInfo from domain name (owner, tokenId, profile)
/api/resolve-name?wallet=GETPrimary domain from wallet
/api/resolve-address?name=GETOwner address from domain
/api/domain/has-domain?wallet=GETDoes wallet own at least 1 .heylogramtv?
/api/domain/image/[tokenId]GETSVG NFT image (dynamic)
/api/domain/metadata/[tokenId]GETOpenSea-compatible metadata JSON
/api/domain/profileGET/POSTRead/write profile (Linktree-style)
/api/domain/websiteGET/POSTDomain redirect URL

Task System

EndpointMethodDescription
/api/missionsGETList of active missions
/api/mission/completePOSTSigned mission completion
/api/mission/content-completePOSTContent creation task (manual approval)
/api/mission/domain-mintPOSTDomain mint tx hash verify → points
/api/user?wallet=GETUser profile + points
/api/user/activity?wallet=GETRecent activity feed

Garura Season System

EndpointMethodDescription
/api/garura/seasonGETActive season info (token, amount, duration)
/api/garura/registerPOSTRegister for season with point commit
/api/garura/claimPOSTEnd-of-season token claim (admin-signed)
/api/cron/season-distributePOSTVercel Cron — distributes ended seasons

URA Click Game

EndpointMethodDescription
/api/ura/clickPOST1 click → 1 URA mint, rate-limited
/api/ura/batch-mintPOSTBulk mint accumulated offline points
/api/ura/leaderboard?limit=GETTop-N clicking wallets
/api/ura/nicknameGET/POSTBind/read nickname for wallet
/api/ura/sessionPOSTStart anonymous session (in-app wallet)
/api/ura/user?wallet=GETUser's URA stats
/api/ura/referralPOSTReferral record (once, anti-bot)
/api/ura/scoreGET(under luck) click score query

Partner / B2B

EndpointMethodDescription
/api/partner?wallet=GETPartner status (live ownerOf check)
/api/partner/activatePOSTPartner activation via domain
/api/partner/analytics?wallet=GETTask click statistics
/api/partner/contentPOSTUpdate task content
/api/partner/deposit-bonusPOSTBonus points on VOI deposit
/api/partner/logoPOSTLogo upload (URL or file)
/api/partner/public-tasksGETAll active partner tasks for tasks page
/api/partner/tasksGET/POSTPartner manages own tasks
/api/partner/track-visitPOSTScript notification — record visit
/api/partner/webhookPOSTHMAC-signed webhook (form/game notification)

Tracker Script (B2B Bot Protection)

EndpointMethodDescription
/api/track/script/[siteKey]GETJS tracker file (partner site embeds)
/api/track/visitPOSTVisit notification (CORS *)
/api/tracker/pingPOSTHeartbeat — user still on page
/api/tracker/tokenGETGenerate tracker session token

Social Verification

EndpointMethodDescription
/api/verify/tweetPOSTTweet URL verification (X share)

Blog & Announcements

EndpointMethodDescription
/api/blog?status=&limit=GETPublished posts, publishedAt DESC, 30s CDN cache
/api/blog/[slug]GETSingle post detail (TR + EN content, tag, cover, body)

OG & Sharing

EndpointMethodDescription
/api/ogGETGeneral OG image (social sharing)
/api/og-enGETEnglish OG image
/api/og/promo/[slug]GETDynamic OG for promo pages
/api/og/uraGETURA share card
/api/og/blog/[slug]GETBlog post share card (dynamic)

Other

EndpointMethodDescription
/api/weatherGETDecorative data for landing page (animation)
Total
Approximately 60+ public endpoints. Management endpoints (/api/admin/*, /api/cron/*) are not listed — they're access-controlled and excluded from documentation (reducing attack surface).

17Phased Deployment

Heylogram TV is largely live on mainnet. The 2 remaining contracts will be deployed in this order. The MintTimelock plan wascancelled — VOI/HEY caps are fully minted and the operational mint is closed (see §4.1–4.2); the proper solution for the "Mintable" warning is now a decision to callrenounceOwnership().

17.1 Phase 6 — LiquidityMining Next

  1. scripts/deployLiquidityMining.js --network polygon
  2. Fund JVOI reward pool: fundRewardPool(amount)
  3. Write backend keeper: listens to deposit txs, creates Uniswap V3 LP within 48h, calls lockPosition
  4. Vercel env: NEXT_PUBLIC_LIQUIDITY_MINING=0x...
  5. Frontend /luck page deposit + claim UX (integrates with the existing Luck tab inside /dapp/dex)

17.2 Phase 7 — Badge ERC-1155

LuckPool V2 is already live (0xd20e…B9Cf) — only Badge remains.

  1. scripts/deployBadge.js
  2. Vercel env: NEXT_PUBLIC_BADGE_CONTRACT
  3. Badge collection creation UI on partner panel

18Audit & Findings

Static analysis report (AUDIT_REPORT.md, 2026-04-28) run via node audit.js. 62 files, 47ms, 288 findings.

SeverityCountDescription
🔴 CRITICAL6Security / money loss
🟠 ERROR33Runtime / logic
🟡 WARNING126UX brittleness
🔵 INFO123Improvements
Total288

18.1 Critical Findings

CodeIssueStatus
K06Replay attack — no timestamp/nonce on partner APIsFixed (5min TTL signature)
K05Topic hash hardcoded — false positive (label confusion)Intentional
K04CORS * — /api/track/visitIntentional (partner sites call it)
K02partners collection queried by walletIntentional (doc ID = domain, where if needed)

18.2 High-Priority Errors

CodeIssueImpact
E09Math without NaN check (6 files)API 500s
E08Firestore query without limit — partner-tasks endpointTimeout + high bill
E05No wallet regex (9 endpoints)Fixed
E02.data()! non-null assertion (11 files)Crash risk
E07Promise.all → allSettled needed (6 files)Unhandled rejection

18.3 New Findings (During Writing)

N1 — MintTimelock transferTokenOwnership not timelocked MOOT

The MintTimelock plan was cancelled on 2026-05-16 (VOI/HEY mint operation closed, see §17). The contract will not be deployed, so this finding no longer applies.

N2 — Dead-code in GaruraPool V2 SHELVED · MOOT

require(voiMiktari % (1e18 / PUAN_VOI_ORANI) == 0 || true, "");
                                                              ^^^^^^^^
                                                          always true

|| true always being true makes the require do nothing. Either forgotten or intentional dummy. Code quality.Solution: remove the line or write the actual check.

N3 — VOI/HEY Pause Issue After Token Ownership Transfer MOOT

Since the MintTimelock transfer never happens, pause authority remains with the admin wallet. This finding no longer applies (see N1).

N4 — LuckPool V2 Random Validator Manipulation ACTIVE (live on mainnet)

LuckPool V2 is live on mainnet (0xd20e…B9Cf) with theregister/claim flow working. Inside_selectPrize, randomness is based onblock.prevrandao, block.coinbase,block.timestamp — Polygon validators may know these values in advance or do minor manipulation.Not economically feasible for low prizes but risk grows as maxAmt (5% of pool, max 1000 tokens) grows.

Solution: Chainlink VRF V2 integration (VRFConsumerBaseV2 inheritance + requestRandomWordsflow). Current V2 is immutable; deploy a new LuckPoolV3and migrate token pools via addPrize.

N6 — Garura Share Double-Cut Risk SHELVED · MOOT

The Garura season system was shelved on 2026-05-16 (see §7) → the claim endpoint is no longer in operational use. The finding is technically true but no longer relevant. A note to be fixed if Garura is reopened.

N10 — DEX Pool-based Quote Precision Loss LOW

In lib/dexAdapters.ts quoteFromPoolBased, for token1→token0 direction:

amountOut = (amountIn * Q192) / num;   // Q192 = 2¹⁹², num = sqrtPriceX96²

No BigInt overflow, but integer division introduces rounding error for small amounts (around ~0.001% — negligible). For large swaps it doesn't compute slippage — no real price impact based on pool depth. When the actual swap is sent, amountOutMinimum protects against slippage (default 0.5% in UI), so economic risk is low.

N11 — findReferencePrice Not Sorted by Liquidity LOW

findReferencePrice queries all venues in parallel, but picks the first non-null result in venue definition order via results.find(r => r !== null). If Uniswap has a low-liquidity pool while Sushi has a deep one, the deeper pool is skipped and a wrong reference price is returned.

Fix: for each venue's result, also fetch pool liquidity and prefer the highest-liquidity one. Used only when opening a new pool, so not critical.

N12 — Sushi V3 Quoter Bypassed Entirely LOW

In quoteFromVenue, the Quoter is only called forvenue.key === 'univ3' (line 279). Sushi V3 (0x64e8…08A7) falls directly to the pool-based fallback. The comment says "Quoter is missing/broken" — if Sushi V3's Polygon Quoter really doesn't work, this is intentional; if it works, an opportunity for better quotes is missed. Action: manually test Sushi V3 Polygon Quoter; if it works, expand the condition tovenue.key === 'univ3' || venue.key === 'sushiv3'.

18.4 Problem Density (Top 5)

Audit Is Stale
The numbers below are from the 2026-04-28 scan. Most files have changed since (mint UI removed from admin, Garura/Tasks/Advertise shelved, DEX added, blog added). For current numbers, re-runnode audit.js.
FileFindings (2026-04-28)
components/PartnerPanel.tsx21
app/hesap/page.tsx18
app/pazar/page.tsx18
app/resolve/[name]/page.tsx15
app/locker/page.tsx15

18.5 Firestore Security Rules

Heylogram TV stores off-chain data in Firestore. Misconfigured security rules could enable point manipulation, partner task tampering, or webhook secret leaks. Core principles infirestore.rules:

General Principle

Only server-side admin SDK writes. No collection allows direct write from the browser. Frontend only reads (and only allowed collections).

Collection Permissions

CollectionFrontend ReadFrontend WriteServer SDK
kullanicilar/Own docNoFull
seasons/Active is publicNoFull
garuraRegistrations/Own docNoFull
domainProfiles/All publicNo (server verifies)Full
partners/All public (logo, tasks)NoFull
partners/{x}/.webhookSecretNo (private field)NoFull
partnerGorevler/Status === 'active'NoFull
missions/Active === trueNoFull
uraClicks/globalPublicNoFull
uraNicknames/Own docNoFull
uraLeaderboard/All publicNoFull
votes/NoNoFull

Webhook Secret Privacy

W12 Audit
The audit report flagged webhookSecret being stored plaintext in Firestore. Solution: in Security Rules,partners/ is readable but thewebhookSecret field is restricted to admin SDK only. In practice, the browser never reads this collection — the/api/partner server endpoint returns a projection without the secret.

Write Validation

Since no direct writes are accepted, the frontend never writes to Firestore. All writes go through signature + timestamp + server validation. Catch: forgetting that the browser has no functional write permission during development — it fails when deployed locally.

19Known Risks & Roadmap

19.1 Open Structural Issues

  • N4: LuckPool V2 random — migrate to Chainlink VRF (for high prizes)
  • N10: DEX pool-based quote doesn't account for slippage on large swaps (slippage UI protects; low economic risk)
  • N11: findReferencePrice uses venue definition order instead of liquidity ranking
  • N12: Sushi V3 Quoter bypassed (intentional if truly broken; missed accuracy otherwise)
  • E02, E07, E08, E09: 33 backend error fixes (deferred audit notes)
  • W04: Partner track-visit / webhook rate limit (bot protection)

No longer applicable: N1/N3 (MintTimelock cancelled), N2/N6 (Garura shelved), N5 (PartnerPool was never used and was removed). These remain in §18.3 as historical documentation.

19.2 CMC Compliance Roadmap

Current CMC status (2026-05): VOI and HEY have three warnings (Contract Not Renounced, Pause, Low Liquidity). Other tokens (JVOI, HLTV, URA, GARURA) have only Low Liquidity — these four are already ownerless/immutable."Mintable" warning is gone — VOI/HEY caps are fully minted, CMC no longer shows that warning.

WarningAffectsSolutionStatus
Contract Not RenouncedVOI, HEYCall renounceOwnership()Decision (flexibility vs safety)
Pause existsVOI, HEYOZ Pausable is inherited — removal requires a new token version (V2 deploy + 1:1 swap)Next version
Low LiquidityAll tokensLiquidity Mining + DEX flow + organic usagePhase 6
MintableCap reached, warning clearedResolved

19.3 Heylogram.tv Vision

The main big project: heylogram.tv live streaming platform. Design is ready (detailed in HAYALLER.md): TikTok Live + Twitch hybrid, fully Web3, HEY token economy, SVG NFT gift system.

Estimate: 6+ months of engineering, requires a team. The HLTV token is ready for this platform — ownerless, immutable, 629M supply.

19.4 CURBAN — AI Image + NFT

Part of the vision: write a prompt, AI generates an image, auto NFT mint. Separate token (CURBAN) economy. Users can keep without sharing if they wish. Currently in planning phase.

Design: web3.jjvoi.com/curbansubpage. AI API availability check → queue → show generated image → "Save as NFT?" → Polygon mint. NFT name = prompt, content = generated image. Pricing: cost + 20-30%, paid in CURBAN token. Garura-style seasonal pool possible.

19.5 Smart NFT Vision

Current HeylogramDomain NFTs are visually simple SVGs. Dreams:

  • Country NFTs: If a user gets turkey.heylogramtv, the NFT is a waving flag animation (SVG + CSS).
  • Name NFTs: When eyup.heylogramtv is taken, the visual is meaning-specific — "Eyup" → metaphor of patience, custom SVG.
  • Future AI link: prompt = word's meaning, output = NFT image.

19.6 Admin Wallet Security

All protocol admin authorities are currently concentrated in a single wallet. Recommended security layer:

  • Gnosis Safe multi-sig (2/3 or 3/5) — contract-level guarantee, no single point of failure
  • Hot wallet kept separate for daily operations; large wallet operations go through Safe signature
  • Hardware wallet usage mandatory

Trade-off: every admin tx requires multiple signatures (delay) and there's a setup cost — but it strengthens operational discipline and significantly reduces admin key compromise risk.

20Glossary

Admin Wallet
Heylogram TV's authorized address: 0x4254…2c70. Currently single owner; multi-sig migration recommended.
Angel Investor
Position type in LiquidityMining requiring minimum 100 POL, 180-day lock, 5% JVOI reward.
Badge
ERC-1155 HeylagramGaruraBadge token. NFT awarded to users at partner milestones.
committedPoints
Points the user committed to the pool when registering for a Garura season (start-of-season snapshot).
earnedDuringSeason
Additional points earned during the season; included in the share computation.
Ecosystem Tokens
One of the six tokens LuckPool V2 looks for in LP NFTs: VOI, JVOI, HEY, HLTV, URA, GARURA.
DEX Aggregator
Heylogram TV's custom-built multi-venue swap+pool layer. Inside /dapp/dex, three venues (Uniswap V3, Sushi V3, QuickSwap V3) are queried in parallel; the venue offering the best price wins. See §3.8.
Garura shelved
Symbolic tree + seasonal reward pool (GaruraPool V2). The season system was halted on 2026-05-16; the contract is still on mainnet but no longer called from the frontend. URA's conceptual root — URA remains as "Garura's Leaf".
havuzBakiyesi
Legacy (Garura V2): The 85% portion in GaruraPool V2 waiting to be distributed to users. The season system was halted on 2026-05-16.
Heylogram TV
Future live streaming platform (heylogram.tv). The HLTV token is for this platform.
kilitliVOI
Legacy (Garura V2): VOI locked in the pool in GaruraPool V2 when tasks were completed; could not be withdrawn by the partner.
lockedUserJvoi
Total JVOI deposited by users in LiquidityMining. emergencyWithdraw cannot touch this amount.
Locker V1
Existing LP NFT locking contract (0x3df6…CA98). LuckPool V2 reads locks via this address. The "Locker" tab on the DEX page operates through it.
Marketplace
HeylogramMarketplace V2 — domain NFT trading layer, 5% commission.
MintTimelock cancelled
Was the planned 48h-queue contract for VOI/HEY/URA ownership transfer. With VOI/HEY caps fully minted, the mint operation has closed; this contract will not be deployed. The remaining path for CMC's "Mintable" warning is renounceOwnership().
Operator
Legacy (Garura V2): An address authorized by admin in GaruraPool V2, able to call gorevTamamlandi but could not withdraw funds.
Pool-based Quote
The DEX adapter's fallback when the Quoter contract fails. Get the pool address from factory, read slot0.sqrtPriceX96, compute the price via (sqrtPriceX96)² / 2²⁰⁰. The main path for Sushi V3 (its Quoter is bypassed).
Venue
A Polygon DEX in the aggregator (univ3 / sushiv3 / quickv3). Each venue is defined with its own quoter, router, and position manager addresses.
Partner
A site owner with a .heylogramtv domain NFT who opens their own tasks and deposits VOI to the pool.
partnerCount
The count of partner collections created so far in HeylagramGaruraBadge (starts at 1).
Random TokenId
Random NFT ID generated via keccak256 in HeylagramGaruraBadge — no 1,2,3 collision issue like domains.
Refund Timeout
In LiquidityMining, if the backend doesn't bind an LP NFT within 48 hours, user can recover funds.
Season
A distribution period with a specific duration + token pool in the Garura system. Fully Firestore-managed.
Soulbound
Doesn't exist in Heylogram TV — all tokens in the project are transferable. (Different from SALKIM.)
tokenIdToPartner
In HeylagramGaruraBadge, which partner collection a tokenId belongs to — used in uri() calls.
Vault
The URA click game contract; the address GARURA was sent to at deployment.
Webhook Secret
Shared secret used for HMAC signing between a partner site and Heylogram TV. Stored plaintext in Firestore (W12 warning).

Heylogram TV — Polygon mainnet · 6 tokens · 16 contracts · open source
Site: web3.jjvoi.com ·  X: @heylogramtv ·  Email: web3@jjvoi.com
Turkish version: /whitepaper/docs