Skip to content
How it works

Everything the contracts do, in plain words

This page describes the protocol as it is deployed. Every number comes from Config.sol, the immutable constants file; nothing here can be changed by anyone after deployment.

01

Characters as tokens

A character is three things created in one transaction: an ERC-20 token (name, symbol and supply chosen by the creator), a permanent market for that token quoted in TRCH, and a 1/1 controller NFT. Whoever holds the controller NFT leads the character into matches, receives its match prizes, season rewards and creator fees, and may retire it.

Traits

Four traits (Strength, Defense, Speed, Focus), each between 1 and 15, must add up to exactly 45 points. Every character also picks one of five elements, one of eight special abilities and a weakness element. Traits are public and immutable. The creator may add a short story to the metadata (stored on chain, shown on the character's page); it never affects a battle.

The elements form a pentagon: Fire → Air → Earth → Water → Shadow → Fire. Each element beats the next two (+1 to its round score) and loses to the previous two (−1). Facing your weakness element costs a further −2.

SpecialEffect
First Strike+4 in round 1 when attacking
Armoropponent's Strength −2 (minimum 1)
Endurance+1 energy per match
Adaptationelement term never negative; weakness does not apply
Pressure+2 in round 1
Finisher+2 in round 3
Counterstrike+8 when your Counter meets an Attack
Balancethe opponent's stance bonus against you is 6 instead of 8
The five-element pentagonFire, Air, Earth, Water and Shadow arranged in a pentagon. Each element beats the next two and loses to the previous two.FIAIEAWASHFireAirEarthWaterShadow3 ROUNDSNO RANDOMNESS

Tokenomics chosen by the creator, bounded by the protocol

ParameterRangeWhat it determines
Supply1,000,000 – 1,000,000,000,000 tokensOnly the price per token; the curve and market cap are the same.
Virtual TRCH reserve1,000 TRCH – 1,000,000 TRCHThe character's starting market cap in TRCH and the steepness of its curve.
Creator allocation0 – 20 %Always vests linearly over 30–365 days through the shared CreatorVesting contract. An unlocked free allocation is impossible.
Creator fee0 – 1 %Extra fee on every trade of the character token, paid to the current controller.
First buyany amount of TRCHThe creator buys on the same curve as everyone else, just first, in the creation transaction.

Creation costs the mint fee of 5,000 TRCH: 50 % is burned, 25 % goes to the RewardPool and 25 % to the treasury. The whole supply minus the vesting allocation goes onto the curve.

Permanent liquidity
There are no LP tokens, no liquidity withdrawal and no migration. The pool cannot be paused and has no owner. Tokens that did not come through the curve (the vested creator allocation) can be sold only while the pool's real TRCH reserve covers them. That reserve is the TRCH that buyers have put into the curve, so a creator with the maximum 20 % allocation can at most take back part of what buyers paid, never more than the pool holds.

The controller NFT is transferable, but not while the character is in an active match or tournament. retire() burns it: the character can never fight again, while its token and market live on; any creator fee accrued afterwards goes to the RewardPool.

02

The TRCH token and the LaunchPool

TRCH is the in-game utility token. Every character market is quoted in TRCH, and TRCH is the only means of payment in the league: character creation, match stakes, tournament entries and season licenses. The total supply of 1,000,000,000 TRCH is minted once, in the token's constructor, and can only shrink through burns.

  • 900,000,000 TRCH (90 %) sit on the LaunchPool curve. Nobody receives TRCH except by buying on the curve or by playing.
  • 100,000,000 TRCH (10 %) seed the RewardPool, released linearly over 730 days so that the first seasons are not a jackpot (see Economy).

The curve

The LaunchPool is a constant-product market between ETH and TRCH with a virtual ETH reserve of 1 ETH: k = (1 ETH + real ETH) × TRCH on the curve. The virtual reserve is a number the curve pretends to hold so that the price does not start at zero; nobody deposits it and nobody can withdraw it. Sellers always receive real ETH only.

  • No fee on buys or sells. No owner, no pause, no migration.
  • Launch cap: for the first 30 days after deployment the cumulative net inflow may not exceed 1 ETH per elapsed day (unused allowance carries over), and a single buy is limited to 0.5 ETH. After day 30 the cap disappears.
  • Sells are never capped. A sell reverts only if the ETH owed would exceed the real reserve, which can happen when TRCH that never passed through the curve (the RewardPool seed) is sold back.
  • The founder's purchase is executed inside the LaunchPool constructor, on the same curve, emitted as a FounderBuy event and not counted against the cap. There is no pre-mint, no team allocation and no presale.

Buying and selling TRCH happens in a separate, open-source LaunchPool interface that anyone can host; this site links to it and never embeds price charts.

03

Character markets

Each character has its own constant-product curve between TRCH and the character token, with the creator-chosen virtual TRCH reserve as the quote-side offset. The mechanics mirror the LaunchPool with two differences:

  • A protocol fee of 1 % in TRCH on every buy and sell, split 50 % to the RewardPool and 50 % to the treasury.
  • An optional creator fee of 0–1 %, fixed at creation, that accrues in the pool and is paid to the character's current controller when anyone calls claimCreatorFees().

Both quotes (quoteBuy, quoteSell) are net of fees and shown on the character page before you trade; the interface applies a 1 % slippage tolerance. A sale reverts if the pool's real reserve could not cover it. Liquidity is permanent: no LP tokens, no withdrawal, no owner, no pause.

Token holders have no claim on match prizes and no yield: the only things a character token does are trade on its curve, trade on its AMM pool and stake on matches.

Secondary market: the AMM pool

Next to its curve, every character can have one AMM pool (TRCH / character token), a constant-product market in the style of Uniswap v2 where anyone can provide liquidity and receive LP shares. The pool is optional and permissionless: anyone may open it from the character page (one per character, no fee), and the first deposit sets its price. The curve remains the primary market and the permanent liquidity guarantee; the pool is where holders can set a different price or earn fees.

  • Every swap pays 0.3 % to the liquidity providers (it stays in the pool, so the product of the reserves never decreases) and 0.05 % to the protocol, always in TRCH, split 50 % RewardPool / 50 % treasury in the same transaction. The protocol never holds character tokens.
  • LP shares are ordinary ERC-20 tokens that redeem a pro-rata part of both reserves at any time. The first 1000 wei of shares are locked forever, so nobody can inflate the share price against later depositors.
  • Pools are always TRCH / character token; trading one character for another is two swaps through TRCH. The factory accepts only tokens created by the CharacterFactory.
  • No owner, no pause, no fee switch, no migration: the fees are constants in Config.sol and the pair contracts are immutable.
04

Matches

Match lifecycle: challenge, accept, commit, reveal, resolve; three rounds decide the winner.1Challengestake escrowed2Accept48 h window3Commithash of tactics4Revealafter staking closes5ResolvedeterministicR1R2R3best of three roundsstaking window: holders back a side, no-loss

Lifecycle

  1. Challenge. A controller challenges a specific character or opens the challenge to anyone, with a stake per side and a preset. The stake is escrowed in the MatchManager. Stake 0 is a friendly match: no escrow, no staking, no rating, no cooldown. Otherwise the stake is between 100 TRCH and 1,000,000 TRCH.
  2. Accept within 48 hours, with the matching stake. The same address may not control both characters. Unaccepted challenges can be cancelled by the challenger or expired by anyone with poke(); the stake is refunded as a claim.
  3. Commit. From acceptance until the commit deadline, each side submits a hash of its tactics (three stances and three energy values plus a secret salt). The interface derives the salt from a wallet signature and stores the tactics encrypted inside the commit event, so you can reveal from any device.
  4. Reveal once both sides have committed and the staking window has closed. The second reveal resolves the match on chain.

Presets

PresetStaking windowCommit windowReveal window
Blitz1 h from acceptancefrom acceptance until 1 h after the staking window closes1 h after both commits are in and staking has closed
Standard24 h from acceptancefrom acceptance until 24 h after the staking window closes24 h after both commits are in and staking has closed
Tournament12 h from acceptancefrom acceptance until 12 h after the staking window closes12 h after both commits are in and staking has closed

Blitz and Standard are chosen by the challenger; the Tournament preset is used for bracket matches.

The battle engine

Three rounds. In each round every character has a stance (Attack, Defense, Counter) and spends energy. The energy budget per match is 5 + Speed / 2 (Endurance adds 1) and must be split across the rounds exactly. The round score is:

score = base(stance) + 2 × energy + stance bonus + element term + special
base(Attack)  = Strength + Focus / 2
base(Defense) = Defense  + Strength / 2
base(Counter) = Focus    + Defense / 2
stance bonus  = +8 when Attack meets Defense, Defense meets Counter, or Counter meets Attack
element term  = ±1 from the pentagon, −2 when facing your weakness

The higher score wins the round; on a tie the faster character wins, and if speeds are equal the round is drawn. More rounds won wins the match; equal rounds is a draw. All arithmetic is integer, in a pure library that the TypeScript port of this site matches bit for bit across more than 200 shared test vectors. There is no randomness anywhere: no block hashes, no oracles, no VRF.

Timeouts, forfeits, voids, draws

  • Commit deadline passes with no commits: the match is void, both stakes are refunded, no rating change, no cooldown.
  • Commit deadline passes with one commit: the side that committed wins by forfeit. A forfeit is a loss for every purpose.
  • Reveal deadline passes: same rule, applied to reveals.
  • A draw refunds both stakes; stakers receive no reward and ratings do not move.
  • Every timeout transition is permissionless: anyone can call poke(matchId).

Payout

The winner's claim is both stakes minus a 5 % rake: 3 % to the RewardPool, 2 % burned, 0 % to the treasury. Prizes go only to the winning controller address and are collected with claim() (pull payment); they are never routed into any character pool. The same pair of characters cannot play a staked match again for 24 hours.

05

Staking

While a staked match is in its staking window, holders of either character's token can stake those tokens on a side. Controllers of either side cannot stake on their own match. Staking is no-loss:

  • After the match resolves, every staker withdraws exactly what they deposited, whatever the outcome, with withdraw(matchId). Withdrawals are never pausable.
  • Stakers on the winning side additionally share a TRCH reward paid only from the RewardPool: 3 % of the match pot (both stakes), capped at 2 % of what the RewardPool has available. The reward is split pro rata to the stake; rounding dust returns to the pool.
  • Draws and voids pay no reward. If the winning side has no stakers, nothing leaves the RewardPool.

The staker reward share (3 %) equals the rake share that enters the RewardPool from that match (3 %), so every match is pass-through for the pool: a match can neither fill nor drain it. This is enforced by a constant in Config.sol.

06

Rating and seasons

Elo

Every character starts at 1000. Staked matches and tournament matches are rated; friendlies are not. K is 32 for a character's first 20 rated matches and 16 afterwards; the expected score comes from an integer lookup table so that the on-chain and off-chain implementations agree exactly. Forfeits count as losses; draws and voids leave ratings unchanged. Only the first 3 matches between the same pair of characters in a season move ratings.

Seasons

Seasons last 28 days, counted from deployment. At each season boundary ratings are squeezed toward 1000: new = 1000 + 0.8 × (old − 1000), applied lazily the first time a character is touched in the new season.

The contract keeps an on-chain top-10 per season. It is updated after every rated result for characters that are eligible; anyone can call nominate(id) to re-evaluate a character that qualified without playing since. Eligibility requires all three:

  • an active season license,
  • at least 10 rated matches in the season while licensed,
  • against at least 6 distinct opponents.

When a season has ended, anyone can call closeSeason(). It freezes the top-10, pulls 20 % of the RewardPool's available balance and books it to the controllers of the listed characters in the split 40 / 20 / 10 / 8 / 6 / 5 / 4 / 3 / 2 / 2 %. Rewards are collected with claim(). Shares of characters retired in the meantime stay in the pool.

The season license

A season license is a refundable deposit of 10,000 TRCH per character, placed by the controller in the Rating contract. It never yields, it never enters any payout, and it can be withdrawn at any time with unlicense(), even while the guardian pause is active. Withdrawing resets the character's season counters, so it loses eligibility for the current season. The deposit travels with the controller NFT: whoever holds the NFT can withdraw it.

Together with the mint fee, the pair cooldown, the three-rated-matches-per-pair rule, the distinct-opponents requirement and the burn on every staked match, the license is what makes farming the season reward with puppet characters unprofitable.

07

Tournaments

Anyone can organise a single-elimination bracket of 8 or 16 characters with an entry stake between 100 TRCH and 1,000,000 TRCH and an organiser fee of 0–2 %. Controllers register one character each; the entry is escrowed in the Tournament contract and refundable until the bracket starts.

  • Registration stays open for 72 hours. A bracket that did not fill by then can be cancelled by anyone, refunding every entry. A full bracket can be started by anyone.
  • Seeding is deterministic by rating (ties: lower character id), with 1 vs 16, 2 vs 15 and so on, so the top seeds meet last.
  • Every round is a MatchManager match with the Tournament preset (12 h windows). Staking and rating apply. advance() is permissionless once all matches of a round are resolved.
  • A drawn pairing is replayed once. A second draw, or a void, is decided deterministically: higher rating, then lower id.

The pot is entry × size. From it the organiser fee and the 5 % rake (3 % RewardPool, 2 % burn) are taken; the rest is the prize pool, split 60 % / 25 % / 7.5 % / 7.5 % between the winner, the finalist and the two semi-finalists, as claims to their controllers. Staker rewards across the whole bracket total 3 % of the pot, spread evenly over the matches and each capped at 2 % of the pool, so tournaments are pass-through for the RewardPool like matches.

08

Economy

SourceDestination
Mint fee 5,000 TRCH50 % burn · 25 % RewardPool · 25 % treasury
Match stakes95 % winner · 3 % RewardPool · 2 % burn · 0 % treasury
Tournament entries95 % prize pool (minus the organiser fee) · 3 % RewardPool · 2 % burn
1 % fee on character markets50 % RewardPool · 50 % treasury
Season license100 % back to the controller; enters no flow
LaunchPoolno fee

The RewardPool

The RewardPool holds TRCH for player rewards. It receives TRCH by plain transfer (the shares above plus the seed) and pays only by formula: at most 2 % of available per match to the StakingVault and 20 % of available per season to the Rating contract. Nobody can withdraw from it otherwise; its two callers are fixed in the constructor.

available = balance − lockedSeed(t), where the 100,000,000 TRCH seed unlocks linearly over 730 days from deployment. Because every outflow is a percentage of what is available, the pool can never be emptied. The seed is the only TRCH that never passed through the LaunchPool curve, so it has no ETH backing there; selling it back into the LaunchPool is possible only as far as the real reserve goes, and this is stated on the Transparency page.

Burns and the treasury

Two sinks reduce supply: half of every mint fee and 2 % of every staked pot. The treasury receives only the three shares fixed in Config.sol (25 % of the mint fee, 50 % of the 1 % trading fee, 0 % of the rake); each share is an independent constant and a share of zero is a valid deployment. All economic parameters are immutable.

09

Security and governance

  • Immutable contracts. No upgradeable proxies, no delegatecall in custom code, no initialize(). A future version would be a new deployment, never a migration of the existing one.
  • No admin. The only privileged function is pause() on three entry points (create a character, challenge or accept a match, stake), callable by the guardian address for 365 days after deployment. After that the pause dies with the guardian. Commit, reveal, timeout, claim, withdraw and unlicense always work, so a pause can never cause a forfeit or freeze funds.
  • No randomness, no oracles. Nothing reads the LaunchPool price; nothing depends on block hashes.
  • Pull payments everywhere, with two documented exceptions: selling into the LaunchPool or a character pool returns the swapped asset in the same call, guarded against reentrancy.
  • Testing. 100 % line and function coverage on the custom contracts, fuzzing with 10,000 runs per property, three invariant suites, shared battle vectors executed in Solidity, TypeScript and Python, and a clean Slither run (no high or medium findings). There is no external audit; the code is public on GitHub and the threat model is part of the repository.
Verify, do not trust
Every address on the Transparency page links to the block explorer, where the verified source can be compared with the repository.
10

FAQ

Do I need ETH?
Yes, a little: players pay their own gas on Base, typically a fraction of a cent per transaction. A Coinbase Smart Wallet (passkey) or any browser wallet works.
Can the outcome of a match be random or manipulated?
No. The engine is a pure function of both characters' public traits and both controllers' committed tactics. There is no block hash, oracle or VRF anywhere, and tactics are hidden by a hash until both sides have committed.
What happens if my opponent never reveals?
After the reveal deadline anyone can poke the match. The side that revealed wins by forfeit; the forfeit counts as a loss for the other side's rating.
I lost the device I committed from. Can I still reveal?
Yes. The salt and the encryption key are derived from a wallet signature, and the encrypted tactics are stored in the commit event. Sign again on any device and the interface recovers them.
Can I lose my stake when staking on a match?
No. Staking is no-loss by construction: withdraw(matchId) returns exactly what you deposited after the match resolves, whatever the result. Winning-side stakers additionally share a TRCH reward paid from the RewardPool.
Who can withdraw from the RewardPool?
Nobody. It pays only by formula: at most 2 % of its available balance per match to the staking vault and 20 % per season to the Rating contract. Its two callers are fixed in the constructor.
Is the season license a fee?
No. It is a refundable deposit of 10,000 TRCH that you can withdraw at any time, in full, even during a pause. It never yields and never enters any payout; it only marks a character as competing for the season reward.
Can a creator rug a character?
Liquidity is permanent (no LP tokens, no withdrawal, no migration), the creator allocation is capped at 20 % and always vests linearly, and the creator's first buy goes through the same curve as everyone else.
What can the guardian do?
Only pause three entry points (create, challenge/accept, stake) for 365 days after deployment. It cannot touch funds, change parameters or stop reveals, claims and withdrawals.
Can the contracts be upgraded?
No. There are no proxies and no admin. A new version would be a separate deployment.
What does the treasury receive?
Exactly the shares fixed in Config.sol: 25 % of the mint fee, 50 % of the 1 % trading fee on character markets, and 0 % of the match rake. Every flow is listed on the Transparency page.
Does holding a character token entitle me to anything?
Only to trade it on its curve or AMM pool and to stake it on matches. Match prizes go exclusively to the winning controller address; token holders have no claim on prizes and receive no yield.
What is the difference between a character's curve and its AMM pool?
The curve is the primary market: permanent liquidity created at character creation, no LP tokens, no withdrawal. The AMM pool is an optional secondary market that anyone can open and fund; liquidity providers hold LP shares, earn 0.30 % of every swap and can withdraw at any time. Both quote TRCH; the Marketplace shows both prices.
What is the Base Sepolia deployment?
A testnet deployment used to exercise the protocol end to end. Testnet tokens have no value.
Is this an investment?
No. TRENCHAMPIONSHIP is a game built for entertainment. Its tokens are in-game utility tokens with no guaranteed value and no promise of profit, nobody operates the contracts, and the project continues only if it succeeds. Read the disclaimer before you play.
11

Glossary

Character
An ERC-20 token, a permanent TRCH market and a controller NFT, created together with public, immutable traits.
Controller NFT
The 1/1 ERC-721 that gives the right to lead a character, receive its prizes and fees, and retire it. Locked while the character is busy.
TRCH
The in-game utility token: the quote currency of every character market and the only means of payment in the league.
LaunchPool
The permanent ETH/TRCH constant-product market with a 1 ETH virtual reserve, no fee and no owner.
Virtual reserve
A fixed quote-side offset of a curve that sets the starting price and steepness; never deposited, never withdrawable.
Character pool
A character's own TRCH/token curve with a 1 % protocol fee and an optional creator fee.
AMM pool
The optional TRCH/token constant-product pair of a character (Uniswap v2 style): 0.30 % of every swap to the liquidity providers, 0.05 % in TRCH to the protocol; anyone can open it and provide liquidity.
LP shares
The ERC-20 token of an AMM pool that redeems a pro-rata part of both reserves; the first 1000 wei are locked forever.
Stake (match)
The TRCH each side escrows for a match; the winner's claim is both stakes minus the 5 % rake.
Staking (no-loss)
Depositing a character's tokens on a side during the staking window; always returned in full, winning side shares a RewardPool reward.
Commit / reveal
Submitting a hash of your tactics first and the tactics themselves later, so neither side can react to the other.
Preset
The time windows of a match: Blitz 1 h, Standard 24 h, Tournament 12 h.
Forfeit
A loss awarded to the side that missed the commit or reveal deadline while the other side acted.
Void
A match in which neither side committed (or revealed) in time; both stakes are refunded and nothing else happens.
Rake
5 % of a staked pot: 3 % to the RewardPool, 2 % burned, 0 % to the treasury.
RewardPool
The contract that holds reward TRCH and pays only by formula (2 % per match, 20 % per season of its available balance).
Season license
A refundable 10,000 TRCH deposit per character required to compete for the season reward; withdrawable at any time.
Elo
The rating system: start 1000, K 32 for the first 20 rated matches, then 16; table-based expected score.
Poke
The permissionless call that applies a timeout: expiry, void or forfeit.
Claim
Pull-payment withdrawal of prizes, refunds, season rewards or tournament prizes booked to your address.
Guardian
The address that may pause three entry points for 365 days after deployment; it has no other power.
Treasury
The immutable address that receives the fixed shares from Config.sol; it has no functions.