ARCWARDocsOpen app
Documentation

Tokens fight for one pool.How that works, and how to build on it.

Chain
Arc testnet · 5042002
To enter
1 USDC
Target, per battle
5,000 USDC
Window
27,923 blocks · 4 h
Pool fee
0.25% · 80% bought back
Parameters read from the chain 04 Sep 2026 07:34 UTC

What this is

ARCWAR is a launchpad where tokens compete for liquidity instead of each receiving their own. Up to six tokens enter a four-hour auction together — a battle opens with as few as two. The one that raises the most opens a Uniswap v4 pool. The money raised behind the others is not refunded and not burned — it is spent buying the winner in that pool, and the winner’s token is what their backers claim.

That single rule is what the rest of this document explains. It is also the only thing about ARCWAR that is unusual; everything else is an ordinary auction and an ordinary Uniswap pool.

How a battle runs

01Queue1 USDC · supply held02Openone window, all entrants03Bidone clearing price04Resolvebiggest raise wins05Finalisepool opens · losers buy in06Claimwinner's token to all
Six moves, all of them permissionless after the first.
  1. 01

    Enter the queue

    A creator pays 1 USDC and their token joins the queue. The whole supply is held by the orchestrator, and they can withdraw and take all of it back right up until a battle claims it. An entry nobody has claimed in about a day (167,000 blocks) goes stale, and anyone may then send it back to its creator, so a quiet market cannot drag an abandoned entry into a battle.

  2. 02

    The queue fills, then holds

    Once enough tokens are waiting, a thirty-minute countdown starts — and every new entrant pushes it out again. Nobody can open a battle out from under a creator who is still telling people about it.

  3. 03

    Open

    Every entrant’s auction is deployed in the same transaction, so they share one window and no entrant gets a head start. The window is 27,923 blocks — four hours at Arc’s block rate.

  4. 04

    Bid

    Bids fill at one clearing price for everyone. Arriving a block earlier buys nothing, and a bid the clearing price passes is refunded rather than filled at a price its owner did not agree to.

  5. 05

    Resolve

    After the window closes anyone can settle it — the keeper is expected to, but the function is permissionless so a battle cannot be stranded by one wallet going quiet. The biggest raise wins.

  6. 06

    Finalise and claim

    A second transaction opens the winner’s pool and spends the losing side’s raise buying into it. Backers then claim: the winner’s own backers claim the token they bid on, everyone else claims their share of what their money bought.

Launching a token

A launch needs a name, a ticker, a picture and one dollar. There is no allocation to negotiate and no vesting schedule to sign: the supply is fixed at a billion, half of it is auctioned, and the half that does not sell becomes the pool’s liquidity if the token wins.

Entry fee
1 USDC, non-refundable
Total supply
1,000,000,000, fixed at mint
Auctioned
500,000,000 — half
Target to graduate
5,000 USDC raised by the battle, not by each token
Window
Four hours, shared with every other entrant
Picture
Up to 12,000 bytes, stored on chain
Description
Up to 500 bytes
The entry fee does not come back

Not even when the battle falls short. A fee you get back is not a deterrent, and the queue is the one place spam costs other creators something.

A creator may also skip the auction entirely and launch instantly, which is a different thing in almost every respect and has its own section below.

Launching instantly

The queue exists so six tokens can be drawn against each other. A creator who does not want the contest can leave it: launchInstant opens the pool in the same transaction, and the token never enters a battle. About a third of the tokens on this chain arrived this way.

What goes into the pool is the whole supply, not half. A battle sells 500,000,000 through its auction and puts the unsold half into the winner’s pool; an instant launch has nothing to sell, so all 1,000,000,000 become liquidity at once.

Cost
The same $1 queue fee, and nothing else
Into the pool
1,000,000,000
Opening price
$0.000001
Opening valuation
$1,000
Currency in the pool at open
$0
The first buyer is the first person to put money in

The pool opens holding token and no currency at all, which is possible because the liquidity sits in a range strictly below the opening price — a range below the price holds only the token side. Nobody has bought anything yet, so there is nothing for the pool to hold but what the creator supplied. Every dollar in it afterwards arrives through a trade.

Nothing here can be refunded, because nothing was staked

A battle can end with money going back: bids the clearing price passed, and every bid in a battle whose entrants fell short between them. An instant launch takes no bids. There is no raise to return and no target to miss — the only money that changed hands is the dollar to enter the queue, and that is spent whichever route the creator takes.

The pool is the same pool a battle winner gets, on the same terms: liquidity nobody can withdraw, a 0.25% fee on every trade, and that fee swept each hour into AW for the accounts whose trading produced it. What an instant launch does not get is the evidence. A token that won a battle has shown that people put real money behind it against five alternatives; a token that launched instantly has shown that somebody paid a dollar. Both open with permanent liquidity, and only one of them has been argued for.

Backing a token

A bid names an amount and a ceiling: the most it is willing to pay per token. It fills at the clearing price, not at the ceiling, so bidding above the market costs nothing extra — the ceiling only decides at what point the bid stops buying.

The auction is continuous rather than first-come. Everyone who fills, fills at the same price, and the price only moves when demand exceeds the supply being issued. There is no curve to snipe and no advantage to being early in a block.

A bid the price passes is refunded, not filled

If the clearing price rises above your ceiling, your bid stops buying and the unspent remainder comes back. You are never filled at a price you did not agree to.

When a token loses

This is the part that differs from every other launchpad, so it is worth being precise about.

A token that was funded but lost does not refund its backers. Its raise is spent buying the winner’s token in the pool that has just opened, and that token is what its backers claim. They came for one launch and leave holding the one that worked. The ratio is fixed at settlement; what moves afterwards is the price when they sell.

A token that raised almost nothing is not a separate case, and this is the part that changed. The target belongs to the battle: what the entrants raise between them is what is measured, and the ruling that comes back is the same for all of them. So a token that pulled in forty dollars inside a battle that cleared five thousand is funded, and its backers hold tokens rather than a refund — carried by the rest of the field.

Refunds happen when the battle falls short, not when a token does. Then nobody graduates, including whichever token raised the most.

What a losing side's money buyswinnerTokens = swap(loserRaise) into the winner's pool

When nothing clears

If no token in a battle reaches the target, there is no winner and no pool. The battle is recorded as abandoned, every entrant’s supply goes back to its creator, and every bid is refunded in full by the auction itself.

Nothing is paid out until the battle has ruled

An auction in a battle cannot answer for itself, because the figure it would have to check is not one it can see. So it is told at the moment it opens to wait, and from then until resolveBattle runs it refuses every exit and every claim. Bids and reserve supply sit where they are.

There is no deadline that releases them, deliberately: a fallback would be a second rule for settling a battle, and the point of the first one is that it is the only one. resolveBattle takes no permission and no key — anyone at all can call it once the window closes, and that is what ends the wait.

Why the pool stays

The winner’s pool position is owned by PoolVault, and that contract has no function that reduces liquidity. Not a locked one, not a time-delayed one — the code to withdraw does not exist. The only modifyLiquidity call in it passes a delta of zero, which is how Uniswap v4 says “settle my fees and change nothing”.

It is also the one contract in the system that is not upgradeable. The orchestrator and the migrator sit behind proxies so their settlement logic can be fixed; the positions were moved somewhere a change of code cannot reach, or “the liquidity is permanent” would have quietly meant “until whoever holds the upgrade key decides otherwise”.

AW and epochs

Trading fees collected from every pool on the platform fund an epoch. Your share of that epoch is your share of the volume you drove during it.

An epoch pays accounts that traded at least $50 in it

Below that, the hour pays nothing, and the volume is not carried: each epoch judges itself from nothing, so five dollars an hour for ten hours is paid in none of them. Fifty dollars in the hour, or nothing. The floor exists because an allocation worth a fraction of a cent costs more gas to claim than it is worth, and every one of them is a leaf in the tree the page has to serve.

There is no premine, no allocation and no reserve. The entire supply was minted to the treasury and put into a pool; the only way AW reaches anyone is the treasury buying it back out with fees that were actually collected. It is the protocol’s own token and it does not compete for a pool with anyone.

Fees

To enter the queue
1 USDC, once, non-refundable
Taken from the raise
0.25% of what a battle raised, once, at settlement
Trading fees
0.25% of every trade, swept hourly
To claim
Gas only

The battle cut is charged on the whole raise — the winner’s and the losing side’s together — and goes to the treasury in USDC. It is a real cost to the losing bidders: the buy-back that funds their claim is 0.25% smaller than their raise. A battle that falls short is refunded in full; the cut is only ever taken from money that graduated.

The raisewinner’s and losers’, at settlementEvery trade0.25% pool fee, swept hourly99.75%Winner’s poolliquidity nobody can withdraw0.25% cutTreasuryUSDC, before any buy80% buybackAW, to the accounts that traded18% social contributorspaid in USDC2% Ctrl88 Labpaid in USDC
A creator who switched the creator fee on takes 0.05% of whatever the protocol took for their token, before the split.
Buyback, to traders
80%
Social contributors
18%
Ctrl88 Lab
2%

Everything the treasury collects — the battle cuts and the hourly sweeps — is divided the same way. The buyback is the share that reaches traders: it is spent buying the protocol’s token, and the tokens bought are handed to the accounts whose trading produced the fees. The other two are paid in USDC rather than in the token, which is deliberate: a recipient paid in the asset whose supply it helps decide has an interest nobody can audit away. The split is a setting on the treasury, capped at 25% for the two paid shares together, and the figures above are what it holds now.

A creator who switched the creator fee on at launch is paid first, out of every sum the protocol takes for their token: 0.05% of the battle cut, and 0.05% of each hourly sweep of their pool. Small by design, and stated so nobody reads it as a mistake.

The whitelist

Before Arc mainnet opens, a spot on the ARCWAR whitelist is claimed at quest.arcwar.gg: three tasks on X — follow, like, reply — and one wallet to receive it. Spots are recorded in the order they arrive, and the list closes the moment mainnet opens, on 16 September.

Every spot comes with a sealed pack of five traits — background, TV style, face, outfit, accessories — drawn by the server the moment the spot is taken, each at one of four rarities. The pack opens on the page; the NFT it shows is minted to that wallet once mainnet is live, with exactly the traits shown.

Protocol

What is deployed, and how to read it.

Addresses, the auction we forked, and the calls an integrator needs.

Network

Chain
Arc testnet
Chain ID
5042002
Native currency
USDC — gas is paid in it
Block time
~0.516s
RPC
https://rpc.testnet.arc.io
First indexed block
59976263
The public RPC allows two concurrent eth_getLogs

Enough to read state, not enough to index. Circle hosts keyless vendor endpoints on the same domain that do not have that limit — rpc.blockdaemon.testnet.arc.io and rpc.drpc.testnet.arc.io. Blockdaemon’s is pruned roughly 400,000 blocks behind head, so a deep backfill still needs Circle’s.

None of them answer a browser. Every public Arc endpoint sits behind bot protection that returns 503 to a page’s own fetch, though curl and scripts pass. A web front-end has to proxy its RPC calls through its own origin, which is what this app does.

Contracts

BattleOrchestratorproxythe queue, battles and settlement0xc844Ae20B3f16223B21BDe69E04Ca240D0c16690
BattleMigratorproxyopens pools, spends the losing raise0x713943F21D8929eFb2abd801c161369F073811d2
PoolVaultnot upgradeableholds every position · no exit0x3C61F47031bBc402237369Ce0e89b12367D0e0f0
PoolGatehookbeforeInitialize · only the migrator opens a pool0xAE899700d7Db5bde5278C0eE76eE1125f37ca000
ArcSwapRouterbuy and sell against a pool0x7C4a655B9F1CD22412c88A9C49dB4B9D3fac5fD0
PoolLensread-onlypool state over eth_call0x2E27b07962029d14cDE41604F81d6eeB420EBB70
BattleTokenimplementationevery launch is a clone0x3AB68DBf7A32bBb8Ff4eA1CE95727b852DbF8Fb9
BidPolicyproxycurrently off0x4c2aD951edcB42B7e85f7A30D4c911dBCF355Db8
RaidToken (AW)tokenthe protocol's own; a billion, all in the treasury's pool0xEf25fE723cb7B4F9241f2eF11FeE73855A774ad3
RaidTreasuryproxysweeps fees, buys the token, publishes the epoch0x5faF25836d09F5c2bbe63204c47fabf5c680292D
Continuous Clearing Auction factoryforkour fork of Uniswap's CCA0x12d2eFc32F13389c23E952cB26C75413a19Ca3f7
Uniswap v4 PoolManagerUniswap’s0x5e4b627b99BC77a29c874ba1ccAe65af5d8D1B19

The auction

The auction layer is Uniswap’s Continuous Clearing Auction, vendored and forked. We neither fork nor deploy a DEX — the pool is Uniswap’s v4, at the address above, passed to the migrator as a constructor argument.

The fork adds one thing: exitPartiallyFilledAndClaimBatch, which settles every bid a caller holds in one auction and pays out in a single transfer. Without it a position of three bids was six transactions. You can check which factory you are talking to without opening a battle:

Confirming the deployed factory is the fork
cast code 0x12d2eFc32F13389c23E952cB26C75413a19Ca3f7 \
  --rpc-url https://rpc.testnet.arc.io | grep -c ffe12763
# 1 — exitPartiallyFilledAndClaimBatch is present

cast code 0xe0648174b6c0860C5e8CFB263C3A0B54D28Fe9FD \
  --rpc-url https://rpc.testnet.arc.io | grep -c ffe12763
# 0 — Uniswap's own factory has no such function

Valuation

A token’s valuation is not a model — it falls out of the auction’s own arithmetic. Half the supply is auctioned and every fill settles at one clearing price, so a fully subscribed auction has raised = price × 500e6, and the pool it opens is priced at that same clearing price across the whole billion.

Fully diluted value, above the floorfdv = raised × 2
Below full subscription at the floorfdv = 1,000 USDC
Floor price
0.000001 USDC per token
Floor FDV
1,000 USDC
Full subscription at the floor
500 USDC raised
A token that raises 5,000 alone
10,000 USDC FDV at open
Ceiling price
0.100001 USDC per token — about 100,000,000 USDC FDV; the app bids at it, so a bid fills at whatever the clearing price becomes

After the pool opens, the losing side’s raise buys into it, which moves the price up before anyone has traded. The figures above are the opening valuation, not the settled one.

Reading state

Pool state is not readable through ordinary getters. StateLibrary computes storage slots and reads them through extsload, so there is no getSlot0 to call over JSON-RPC. Two doors exist instead: the indexer’s public API, which derives every price from the pool’s own swap events and answers over HTTPS; and PoolLens, a stateless contract that runs the same slot arithmetic the pool manager does and reports what a position is owed — the read a keeper needs before deciding what is worth sweeping.

A token's price and valuation, from the indexer
const base = "https://testnet.arcwar.gg/idx";
const t = await fetch(`${base}/tokens/${token}`).then((r) => r.json());

// sqrtPriceX96 is tokens per unit of currency, so it runs backwards to value.
const r = Number(BigInt(t.sqrtPriceX96)) / 2 ** 96;
const price = 1 / (r * r);          // USDC per token
const fdv = price * 1_000_000_000;  // supply is fixed at a billion
sqrtPriceX96 is inverted here

It encodes tokens per unit of currency, so squaring it gives you the reciprocal of the price. Sorting tokens on the raw value ranks them backwards — cheapest first — and the mistake looks plausible enough to survive review.

The indexer's API is public

https://testnet.arcwar.gg/idx is the same read the app makes: /tokens, /tokens/:address, /battles, /battles/:id, /queue, /swaps. JSON, no key, cached for a few seconds; it is derived from logs, so it is the same answer a pruned node cannot give you.

Events to index

TokenRegistered
A creator entered the queue. Carries name, symbol, image.
BattleOpened
Auctions deployed. Carries the entrant list and the window.
ClearingPriceUpdated
The live valuation moved. Emitted by each auction.
EntrantSettled
Per token: what it raised, and the battle's verdict — the same for every entrant.
BattleResolved
The winner, its raise, and the losing side's total.
BattleFinalized
The pool is open and the losing raise has been spent. Claims start here.
BattleCutTaken
The 0.25% taken from the raise, and the creator's slice of it.
EntryWithdrawn
A queued token went back to its creator — withdrawn, or evicted once stale.
BattleAbandoned
The battle raised less than the target between them. Everything is refunded.
InstantLaunched
A pool opened without an auction.
Swap
A trade through a pool. Uniswap's, on the PoolManager.
Derive from events, never from historical eth_call

A pruned node answers eth_call at an old block with an error that reads like an empty result. Anything an indexer needs should come out of a log.

Errors

BidMustBeAboveClearingPrice
The ceiling named is at or below where the auction has already settled.
CannotExitBid
This bid set the clearing price. It leaves through exitPartiallyFilledBid.
BidNotExited
claimTokens was called before the bid was settled.
NotGraduated
The battle fell short, so there is nothing to claim — only to refund.
VerdictPending
The battle has not been resolved yet. Nothing settles until it is; anyone can call resolveBattle once the window closes.
AlreadyExited
This bid has already been settled.
NothingToClaim
Settled, and the payout was zero.
BattleStillRunning
The window has not closed yet.
BattleNotResolved
finalizeBattle was called before resolveBattle.
NotEnoughEntrants
The queue is too short to open a battle.
CountdownRunning
The queue's hold has not expired.
WrongCreationFee
The value sent is not the current creation fee.
ImageTooLarge
The picture is over 12,000 bytes.
DescriptionTooLong
The description is over 500 bytes.
EntryStillFresh
Only the creator can withdraw an entry younger than 167,000 blocks.