TokenFlow Documentation
TokenFlow (TFLO) is an Ethereum-mainnet ERC20 with a single on-chain community treasury. 100% of the supply is seeded into one Uniswap V4 pool at launch — no presale, no team allocation, no vesting. Pool fees fund the treasury, which rotates through a team-curated basket via a deterministic round-robin queue and burns TFLO on every profitable sale.
The whole protocol in one sentence. Trades pay a 4% pool fee → 80% accrues to the treasury, 20% to the team → once the treasury holds ≥ 0.5 ETH, anyone can trigger the next buy → tranches sell when their 30-minute TWAP gain clears 10% → all ETH from sales buys TFLO and burns it.
At a glance
| Token | TFLO — ERC20 with burn, 1,000,000,000 total supply |
| Chain | Ethereum mainnet only (chainId 1) |
| Pool | Uniswap V4, TFLO / WETH, 4% fee |
| Fee split | 80% → treasury, 20% → team wallet |
| Buy trigger | Treasury ≥ 0.5 ETH (configurable 0.1–10) |
| Sell trigger | Tranche 30-min TWAP gain ≥ 10% (configurable 2%–50%) |
| Sale proceeds | 100% buy & burn TFLO — supply only shrinks |
| Eligible list | Team-curated, capped at 50 tokens |
| Admin surface | 4 owner functions, hard-coded bounds, no withdraw / pause / upgrade / mint |
How it works
Fee flow
Every TFLO swap on the Uniswap V4 pool pays a 4% pool fee. Those fees accumulate inside the pool position as the protocol's reward for providing the LP. A dedicated contract holds the LP NFT and exposes a single function — harvest() — that anyone can call at any time.
When harvest() fires, it collects accrued fees (and only fees — the principal liquidity is unreachable by anyone, including the team), swaps the TFLO portion to ETH through the same TWAP-guarded router used everywhere else, and routes the result:
- 20% → team wallet (push, with pull-fallback if the push reverts)
- 80% → on-chain treasury
The LP NFT is permanently owned by the harvester contract. There is no transferNFT, no permit, no path that ever extracts the liquidity itself.
Round-robin buys
The treasury holds ETH and tracks a single integer — a public round-robin cursor — that determines the next eligible token to buy. The decision is mechanical: nextBuyIndex % eligibleTokens.length at the moment of the call. There is no oracle, no off-chain selection, no caller-supplied target.
Once the treasury balance crosses the buy threshold (0.5 ETH by default), anyone can call triggerBuy() in a single transaction. The contract picks the cursor's current target, swaps ETH for that token through a Uniswap router with a TWAP-bounded slippage check, records a tranche for what was bought, and advances the cursor by exactly one.
Why a fixed order? A public, deterministic queue gives every eligible token equal treatment, leaves no room for the team to favour or punish any token, and removes the usual front-running games — because the next buy is already common knowledge.
Cursor walk-through
Eligible list [A, B, C, D], cursor starting at 0:
| Call | Index used | Token bought | Cursor after |
|---|---|---|---|
| #1 | 0 | A | 1 |
| #2 | 1 | B | 2 |
| #3 | 2 | C | 3 |
| #4 | 3 | D | 4 |
| #5 | 4 % 4 = 0 | A again | 5 |
Each full rotation buys every eligible token exactly once. Allocation across the basket converges to equality.
Per-buy spend cap
A single triggerBuy() spends at most 2× the buy threshold in ETH, even if the treasury holds more. No single call can drain the treasury, and MEV exposure is bounded by both the cap and the 150 bps TWAP guard on the swap itself.
Tranches & sells
Each successful buy is recorded as an immutable tranche: the token bought, the amount received, the ETH spent, the timestamp, and a sold flag. Tranches are append-only — they're never compacted, reordered, or deleted.
A tranche becomes sellable once its 30-minute TWAP price clears the sell-gain threshold (10% over basis by default). Anyone can call executeSell(token, trancheIndex) on any tranche that's eligible. There is no FIFO ordering — older tranches don't have to sell first, and the caller picks which tranche to act on. Each tranche can be sold at most once; the contract flips sold = true before any external interaction.
Sells don't touch the buy cursor. The next triggerBuy() proceeds against whatever the cursor points at, independent of what just sold.
Buyback & burn
Every ETH proceed from a sell is hard-wired to a single destination: a dedicated buyback contract that swaps ETH for TFLO through the V4 pool (with the same TWAP guard) and calls burn() on the received TFLO.
Supply only ever shrinks. There is no path — owner or otherwise — that routes sell proceeds anywhere except to buy and burn TFLO. The treasury cannot be withdrawn, and the buyback cannot be bypassed.
Parameters
Immutable constants
Set in the contract constructors and never changeable. Modifying any of these requires a new deployment.
| Parameter | Value |
|---|---|
| TFLO total supply | 1,000,000,000 TFLO |
| Pool fee | 4% (40,000 in V4 fee units) |
| Treasury share of fees | 80% |
| Team share of fees | 20% |
| TWAP window | 30 minutes (1,800 seconds) |
| Max price impact vs TWAP per swap | 150 bps (1.5%) |
| Max eligible tokens | 50 |
| Max ETH per buy | 2 × buy threshold |
Configurable bounds
Two parameters can be tuned by the owner — but only within hard-coded ranges that are themselves immutable. The setters revert on any value outside these limits.
| Parameter | Default | Min | Max |
|---|---|---|---|
| Buy threshold (ETH) | 0.5 | 0.1 | 10 |
| Sell gain (% over basis) | 10% | 2% | 50% |
Widening either range — for instance, allowing a 20-ETH buy threshold — would require a v2 deployment, since the bounds themselves are coded as immutable constants and cannot be changed by any function.
Eligible tokens
How tokens get added
The eligible list is curated entirely by the team. The community surfaces candidates through an off-chain leaderboard, but no chain action ever happens automatically.
- Community proposes. Anyone signed in with X can propose any ERC20 address and vote on existing proposals. The leaderboard is public.
- Team audits. Before adding anything on-chain, the team runs an audit pass on the candidate — checking honeypot/transfer-tax signals, deepest pool across Uniswap V2/V3/V4, and minimum TVL so a buy stays inside the 150 bps TWAP guard.
- Owner adds. If the audit passes, the owner calls
addEligibleToken(address, kind, pool)directly. There is no on-chain proposal contract, no proposal bond, and no automatic promotion from the leaderboard.
Add / remove behavior
Adding a token simply appends it to the eligible array; the buy cursor doesn't move. The new token is first reached when the cursor naturally rotates past its slot — meaning a freshly added token gets its first buy on its turn, not immediately.
Removing a token disables future buys of it. The token is taken out of the eligible array, but the contract intentionally never clears the data needed to unwind existing holdings: the pool kind, the canonical pool address, and all existing tranches stay on-chain forever. Already-bought tranches of a removed token remain sellable indefinitely, against the same TWAP gain threshold, with proceeds still routing to buyback & burn.
The team can stop the protocol from buying a token, but they cannot trap funds inside one.
The 50-token cap
The eligible list is hard-capped at 50 entries. addEligibleToken reverts when the array is full and when the token is already present. The cap exists to keep removal lookups bounded and to keep view-function enumeration cheap; it is set as an immutable constant.
Community leaderboard
X sign-in
The leaderboard is an off-chain product hosted alongside the dApp. Authentication is via X (Twitter) OAuth — sign in with X and you can propose tokens and vote. The protocol stores only the X handle, name, avatar, and a session cookie; nothing else is collected.
The leaderboard is purely advisory. It does not move funds. It does not auto-promote any token on-chain. It is a public signal that the team uses when curating the eligible list — nothing more.
Propose & vote rules
- Propose any ERC20 address. Idempotent — proposing an address that already exists is a no-op.
- Vote for any proposed token. One vote per X account per token; clicking again toggles it off. No proposal bond, no vote weight, no quadratic anything.
- Audit badge. Each proposal shows the team's audit status (passed / flagged / not yet audited) so voters know what's been reviewed.
Security
Guarantees
These properties hold by code, not by promise. They form the audit checklist for the contracts.
- Narrow admin surface. The owner has exactly four state-changing functions on the treasury: tune the buy threshold (inside [0.1, 10] ETH), tune the sell gain (inside [2%, 50%]), add an eligible token, remove an eligible token. There is no pause, no withdraw, no upgrade, no mint, no admin escape.
- LP non-extractable. The contract that owns the Uniswap V4 LP position exposes only
harvest(). There is no path that withdraws principal liquidity — only fees on top of it. - TWAP enforcement on every swap. Every router call computes minimum output from the 30-minute TWAP, passes it to Uniswap, and re-checks the executed price after the swap. Anything outside ±150 bps of TWAP reverts.
- TWAP freshness. Swaps and sells revert if the 30-minute window can't be observed (insufficient V3/V4 history, or an idle V2 pair).
- Deterministic round-robin. The next buy target is fixed by an integer cursor read at the moment of the call. No caller input influences which token is bought, and the per-buy ETH cap (2× the trigger) bounds the worst-case MEV exposure.
- Eligible-token cap. Hard limit of 50 tokens in the eligible array, enforced on every add.
- Reentrancy guards on buy and sell. ETH transfers are explicit, checked calls.
- No double-sell. Each tranche flips to
sold = truebefore the buyback runs. A tranche can be sold at most once. - Removed-token sells remain possible. Removing a token never clears the per-token state that
executeSellreads. Historical holdings can always be unwound. - No external price oracles. Uniswap TWAP is the only price source for trade-decision logic. No Chainlink, no off-chain feeds.
- No
selfdestruct, nodelegatecallto user-controlled addresses, no unvetted inline assembly. - Immutable external addresses. Uniswap routers, V4 PoolManager, and WETH are constructor params that can never be changed.
- Bounded work per buy.
triggerBuy()is O(1) in the eligible-list length. No loop over the basket, no scan of tranches. - Buyback non-bypassable. All ETH proceeds from sells go directly to buyback & burn. No
withdraw, including to the owner.
The v1 trade-off
v1 ships with the deployer as the sole owner — not a multisig, not a timelock. This is an intentional trade-off, and it's bounded by the rest of the design.
A compromised owner key can do exactly four things:
- Move the buy threshold inside [0.1, 10] ETH
- Move the sell gain inside [2%, 50%]
- Add a token to the eligible list (up to the cap of 50)
- Remove a token from the eligible list
What a compromised key cannot do: withdraw treasury funds, mint TFLO, pause the protocol, upgrade any contract, extract the LP, bypass the buyback, or sell a tranche outside the TWAP gain rule.
Ownership is transferable through the standard OpenZeppelin transferOwnership path. Moving to a multisig — and later to a timelock — requires no contract changes. The blast radius of a key compromise is bounded enough that operating with a single key while the protocol is small is an acceptable trade for being able to iterate on the eligible list.
Reference
Contracts
All contracts are Solidity 0.8.28 with OpenZeppelin Contracts 5.x. Sources are verified on Etherscan at deploy.
| Contract | Purpose |
|---|---|
| TokenFlow | The TFLO ERC20. Burnable. 1B supply minted in the constructor for LP seeding. No mint after construction. |
| LaunchpadV4 | One-shot launch helper. Initializes the V4 pool, mints the full-range LP NFT, hands the NFT to the fee harvester. |
| FeeHarvester | Permanent owner of the LP NFT. Only callable path is harvest() — collects fees, swaps to ETH, splits 80/20 to treasury and team. |
| Treasury | Holds ETH and tracks holdings as tranches. Maintains the eligible list and round-robin cursor. Hosts the four owner functions. |
| SwapRouter | Stateless wrapper around Uniswap's Universal Router with TWAP slippage checks before and after each swap. |
| PriceGuard | 30-minute TWAP abstraction across Uniswap V2 / V3 / V4. Reverts if the window can't be observed. |
| BasisLedger | Library. The tranche data structure and the gain-vs-basis math used by sells. |
| TFLOBuyback | The only destination for sell proceeds. Swaps ETH for TFLO and burns it. |
External addresses
Mainnet dependencies, to be pinned in the contracts at deploy time.
| Name | Address |
|---|---|
| WETH9 | 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 |
| Uniswap V4 PoolManager | Pinned at deploy |
| Uniswap V4 PositionManager | Pinned at deploy |
| Uniswap Universal Router (v2.5) | 0x66a9893cC07D91D95644AEDD05D03f95e1dBA8Af |
| Uniswap V2 Factory | 0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f |
| Uniswap V3 Factory | 0x1F98431c8aD98523631AE4a59f267346ea31F984 |
| TFLO | Published at launch |
| Treasury | Published at launch |
Glossary
- TFLO — TokenFlow's ERC20 token. Ethereum mainnet, 1B supply, burnable.
- Treasury — The on-chain contract that holds ETH from pool fees and the tranches of bought tokens.
- Eligible tokens — The team-curated list of tokens the treasury is allowed to buy. Capped at 50.
- Round-robin cursor — A monotonic integer on the treasury. The next buy targets
eligibleTokens[cursor % length]and advances by 1 on success. - Tranche — One buy event. Records the token, amount received, ETH spent, timestamp, and a
soldflag. Sell-eligibility is computed per tranche; tranches may be sold in any order. - TWAP — Time-weighted average price over a 30-minute window. The only price source the protocol uses for trade decisions.
- Buy threshold — Treasury ETH balance required before
triggerBuy()can fire. Default 0.5, owner-tunable inside [0.1, 10]. - Sell gain — Minimum TWAP gain over a tranche's cost basis before it becomes sellable. Default 10%, owner-tunable inside [2%, 50%].
- Buyback & burn — The only destination for ETH from sells: swap to TFLO, then call
burn(). - Community leaderboard — Off-chain X-OAuth UI for proposing and voting on tokens. Purely advisory; never moves funds.
For questions: Telegram · X (Twitter)