Docs
Arctis is a launchpad on Robinhood Chain. Tokens launch through Pons and trade against ETH. Their trading fees are spent by an AI agent on a basket of real-world asset tokens, which is pushed to holders every three minutes.
Overview
A creator launches a token and picks one to three approved RWA reward assets, with weights totalling 100%. From the first trade, Pons charges fees on the ETH side of the market. Those fees do not go to the creator's wallet — they go to a contract that converts them into the chosen RWAs and sends them to the people holding the token.
How that ETH gets spent is not a fixed formula. An AI agent reads recent prices for the basket's assets and decides the split for the hours ahead, including whether to sit on some ETH rather than buy into a spike. Its room to move is bounded on chain and the bounds are set at launch, so the furthest it can stray is a modest distance either side of the weights the creator chose.
The RWAs are a reward, not a trading pair. Every Arctis market is TOKEN / ETH regardless of what its basket contains.
How it works
01
Launch on Arctis
Create your token and choose up to three supported RWA reward assets.
02
Trade against ETH
Every Arctis token launches through Pons with an ETH market.
03
Fees accumulate in ETH
Trading generates creator fees that flow into the token’s Arctis reward contract.
04
An agent buys the basket
Every three minutes, accumulated ETH is converted. An AI agent sets the split and can hold some back for a better entry, inside limits fixed at launch.
05
Holders receive RWAs
Arctis distributes the purchased RWA tokens automatically. No staking. No claiming. No claim fees.
The reward cycle
The same five steps, in protocol terms. Each launch runs its own three-minute clock:
- Trading on Pons accrues ETH creator fees, credited to the launch's
BasketDistributor. - When the window closes, a keeper calls
processEpoch. The distributor pulls its accrued ETH out of the Pons fee escrow. - The ETH is split — by the agent's standing plan, or by the declared weights when there is no valid one — and swapped for each RWA through
RewardRouter, which only follows pre-approved routes. - The purchased RWAs are moved to
AutoAirdropDistributorand recorded as that epoch's reserve. - The airdrop worker computes each holder's share and submits it in batches. The contract transfers the tokens out.
How Pons is wired
Arctis launches through PonsV2LaunchFactory on Robinhood Chain (chain id 4663). Two details carry the whole integration.
The fee recipient. Pons' launch parameters include a creatorFeeRecipient. Arctis sets it to the launch's own BasketDistributor. That single field is what makes Pons pay holders instead of the creator, and Pons fixes it at creation — so a creator cannot redirect the reward stream after the fact.
ETH as the quote asset. Arctis always passes the zero address as the pair token, which is how Pons expresses a native-ETH market. Fees therefore arrive in one uniform asset for every launch.
Pons credits fees to an escrow rather than pushing them, and its claim() takes no arguments — it pays whoever calls it. So each distributor pulls its own balance during processEpoch; nothing can claim on its behalf, and a keeper has no recipient or calldata to manipulate.
The RWA basket
A basket holds one to three assets from a curated registry, with weights in basis points that must total exactly 10,000. The assets are fixed at launch and cannot be changed afterwards, by the creator or by protocol admins — not by the agent either, which chooses how much of each to buy but never what to buy.
The weights are the creator's declared split, and they remain the reference the agent is measured against. It may move within a bounded range around them, described in the agent.
Only assets in RewardRegistry can be chosen, and each must have an approved swap route. A creator cannot point a basket at an arbitrary token address.
The agent
Each launch is paired with an agent that decides how its fees are spent. Every six hours it reads recent prices for the basket's assets and answers two questions for the epochs ahead: how to weight the legs, and whether to hold ETH back instead of buying now. Between those decisions the plan simply stands, so the model is consulted on the timescale a view actually changes rather than once per three-minute epoch.
Its discretion is small on purpose, and the limits are enforced by BasketDistributor rather than by the agent itself. The contract rejects a plan that breaks any of them, so an agent that is wrong, broken, or compromised still cannot spend outside these bounds:
| Bound | Limit |
|---|---|
| Weight drift | 30% of each declared weight |
| ETH held back | 0.05 ETH per launch |
| Consecutive epochs without buying | 20, roughly an hour |
Drift is measured relative to each declared weight rather than as a flat allowance, so a large leg is given proportionate room and a small one is not handed enough to double. A leg declared at 50% may sit anywhere from 35% to 65%; a leg declared at 10% moves only between 7% and 13%. Whatever the agent picks, the legs still have to total 100%.
The two remaining bounds are what stop “wait for a better price” from becoming “never buy”. Only a small amount of ETH can sit idle, and after twenty held epochs the launch must deploy its fees regardless of what the agent thinks. Holding is a pause, not a veto.
Nothing here can stall a launch. A plan that fails validation is discarded rather than repaired, and that epoch runs on the creator's declared weights instead — quietly clamping a bad answer would execute a split nobody chose. A plan also expires after 24 hours, so if the agent stops producing decisions the launch falls back to the creator's split on its own. Before submitting, the engine simulates the plan against live prices, so an allocation that could not actually be swapped never becomes a stuck epoch.
Every decision is stored with the model's reasoning and the prices it saw, so any basket a launch bought can be traced back to the plan that caused it.
Multiple launches
With many launches running at once, nothing about a distribution is global. Every launch gets its own BasketDistributor clone, and Pons pays that specific contract, so fee streams never mix: a token's ETH physically lands in its own distributor.
Holder rewards are held in one shared AutoAirdropDistributor, but every record inside it is keyed by the launched token first:
| Record | Keyed by |
|---|---|
| Epoch reserve | token, epoch, reward asset |
| Pending holder balance | token, holder, reward asset |
| Allocation already processed | token, epoch, holder |
| Batch already submitted | token, epoch, batch |
Two guards keep those namespaces honest. Only a launch's registered distributor may credit that launch's reserves, and no allocation can exceed the reserve recorded for its own token and epoch. This matters because the reward assets themselves sit in one pooled contract balance: if two launches both reward the same asset, they share an ERC-20 balance, and it is the per-launch accounting that stops one from spending the other's share.
The holder list itself is not computed on chain. The indexer follows ERC-20 transfers for each launched token and keeps balances per token and wallet, so a wallet holding two Arctis tokens has two independent balances and earns each basket separately. At epoch close, the worker reads the snapshot for that one token, allocates pro-rata by balance, and submits the result as an explicit list of recipients and amounts.
So a holder of token A receives only token A's basket, in proportion to their share of token A. Holding both tokens simply means being included in two separate distributions.
Who is eligible
Eligibility is a token balance at the epoch snapshot. There is no staking, no lockup, and no registration. Allocation is strictly pro-rata: your share of the basket equals your share of the eligible supply.
A small set of addresses is excluded explicitly — the zero and burn addresses, the protocol's own contracts, and the launch's Pons bonding curve. The curve matters most: Pons mints the entire supply to it, so until the launch graduates the curve is by far the largest holder. Leaving it in would hand nearly every reward back to the venue.
Why there is no claim
Rewards are pushed, not claimed. There is no claim() function for holders to call, which means no gas cost, no forgotten rewards, and no advantage for whoever watches the chain most closely.
Two details make pushing safe. A reward below a per-asset dust threshold stays pending and accumulates instead of being sent, so tiny balances do not cost more in gas than they are worth. And a transfer that fails — a token that reverts on a particular recipient, for instance — is isolated: that holder's balance stays pending and the rest of the batch still goes out.
The $ARCTIS exception
The protocol's own $ARCTIS token follows a different economic model. Its creator fees go to the protocol treasury rather than buying an RWA basket, so it never gets a BasketDistributor and never distributes RWA rewards. The launchpad rejects any attempt to give it one.
The contracts
Six contracts are deployed once on Robinhood Chain and shared by every launch, listed here in the order a launch touches them. Every address is shown in full and links to the explorer, so nothing on this page has to be taken on trust.
ArctisLaunchpad
Creates launches
The only contract a creator calls directly. One transaction validates the basket, creates the launch's reward contract, launches the token through Pons, records the basket, and optionally spends part of the sent ETH buying the creator their own token. Doing all of it in one call is what stops anyone from buying a launch before its creator can.
PonsAdapter
Talks to Pons
Every call Arctis makes to Pons goes through here and nowhere else, so Pons' interface is not spread across the codebase. It forces ETH as the quote asset, sets the launch's fee recipient to its reward contract, and buys on the bonding curve for a dev buy. If Pons ships a new version, this is the piece that changes.
RewardRegistry
The approved RWA list
The curated list of RWA tokens a basket may contain, with the decimals of each. Creators pick from it rather than pasting an address, so a basket cannot be pointed at a token nobody can trade, or at a fake one wearing a real ticker.
BasketDistributorFactory
Mints reward contracts
Deploys one cheap clone of BasketDistributor per launch and initialises it with that launch's basket. Clones make a dedicated contract per launch affordable, which is what allows fee streams to stay physically separate instead of being tracked as balances in one shared pot.
RewardRouter
Buys the RWAs
Swaps a launch's accrued ETH for its basket through Uniswap V3, following only routes an admin has approved in advance. The keeper that triggers a swap supplies nothing but a price floor and a deadline — never a path, a recipient, or calldata — and output always returns to the distributor that called it. The worst a stolen keeper key can do is submit a swap that reverts.
AutoAirdropDistributor
Pays holders
Holds the purchased RWAs and transfers them out to holders in batches. It has no claim() at all. Rewards too small to be worth their own gas stay pending and accumulate, and a transfer that fails for one holder does not take the batch down with it.
One per launch
BasketDistributor is the exception: there is a separate one for every token, created at launch, and its address is shown on that token's page. It is the contract Pons pays. It holds that launch's ETH, runs its three-minute clock, and on each epoch pulls its fees out of the Pons escrow, splits them by the basket weights, sends them through RewardRouter, and hands the purchased RWAs to AutoAirdropDistributor. Its basket is fixed at creation and cannot be changed by anyone, including protocol admins. Neither the creator nor an admin can withdraw from it — there is no function to do so.
Supporting code
BasketLib holds the basket rules, so the same validation applies wherever a basket is read: one to three assets, no duplicates, no zero weights, every asset in the registry, and weights summing to exactly 10,000 basis points. V3Path encodes Uniswap V3 swap paths and checks that an approved route really starts at WETH and ends at the asset it claims to buy. ArctisTreasury receives the native token's fees and is deliberately never attached to a launch, which keeps protocol revenue and holder rewards from touching the same accounting.
Retired
The launchpad and the adapter are not upgradeable, so changing them means deploying new ones. These are the versions they replaced. They can no longer create launches, but the tokens they did create are unaffected and still earning: a launch's rewards run through its own distributor, which does not depend on the launchpad that created it.
Roles and permissions
Two keys sign continuously from a server: a keeper that closes epochs and a worker that sends the airdrops. Both are deliberately limited, because a server key is the one most likely to be stolen.
The agent holds neither a key nor a role. It writes plans to a database; the keeper is what carries one on chain, and the distributor bounds-checks it on arrival. So the agent's influence stops at choosing a point inside a range that was fixed when the token launched.
| Role | Can do |
|---|---|
KEEPER_ROLE | Close an epoch and trigger its swaps, supplying only price floors, a deadline, and a plan the contract bounds-checks |
AIR_DROP_ROLE | Submit holder allocations, bounded by the reserve recorded for that token and epoch |
RWA_MANAGER_ROLE | Add or disable assets in the registry |
ROUTER_MANAGER_ROLE | Approve the swap route used to buy an asset |
PAUSER_ROLE | Pause launches or distribution |
LAUNCHPAD_ROLE | Create and register a launch's reward contract. Held by the launchpad itself, not by a person |
What no role can do matters more. Nobody can change a basket after launch, redirect where a launch's fees go, withdraw ETH or RWAs from a distributor, or pay a swap out to an address of their choosing. Those are not permissions that are withheld — the functions do not exist.
LAUNCHPAD_ROLE across, which leaves existing launches running on the contracts they were created with.
