Integrations
A coin here is an ordinary ERC-20 in an ordinary Uniswap V3 pool on Arc (chain id 5042). If your tooling speaks Uniswap V3, it already speaks Arc Perp.
Not deployed yet. Arc Perp's own contracts are not on Arc yet; their addresses will be published in Architecture at deployment, with verified sources on Arc's explorer. The Uniswap, USDC and edgeX addresses on this page are live on Arc today.
The chain, for integrators
- Chain id
5042. Public RPCs includehttps://rpc.mainnet.arc.io(rate-limited),https://rpc.drpc.mainnet.arc.io,https://rpc.blockdaemon.mainnet.arc.ioandhttps://rpc.quicknode.mainnet.arc.io; for anything serious, use a keyed endpoint. - Log ranges depend on the provider. On 2026-09-16 one free tier refused
eth_getLogsover more than about 100 blocks while another accepted 50,000 but capped the results at 20,000. Size your scan window to your provider. - Blocks every ~0.5 s, final in under a second. Timestamps have one-second resolution, so several blocks can share one. Key anything time-based on block numbers.
- Gas is USDC. Fees are EIP-1559 with a base-fee floor of 20 gwei; a transaction whose
maxFeePerGasis below it is silently dropped rather than rejected. - Multicall3 is at the usual
0xcA11bde05977b3631167028862bE2a173976CA11. viem's built-inarcchain ships without RPC URLs, explorer or Multicall3, so define the chain with them. - USDC is logged twice. Every native USDC movement emits an 18-decimal
Transferfrom the system address0xffffFFFfFFffffffffffffffFfFFFfffFFFfFFfE, and ERC-20 calls emit the usual 6-decimalTransferfrom the USDC contract. Filter on the emitter0x3600…0000, or you will count some dollars twice.
Trading
Route through the canonical SwapRouter02 at 0x53bf6b0684ec7ef91e1387da3d1a1769bc5a6f77 with exactInputSingle, fee tier 10000 (1%).
- Before the bond:
COIN / USDC - After:
COIN / TRACKER
USDC is 0x3600000000000000000000000000000000000000 (6 decimals). Pay with the token and an approval. There is no wrapped native token on Arc — Uniswap's periphery contracts there point it at a stub that reverts — so a swap sent with native value, or a route that wraps or unwraps, fails. There is no other asset to route through: every coin is one hop from USDC before its bond, two after.
After the bond, a USDC buy is USDC → TRACKER → COIN: first the tracker's own USDC pool at the 0.05% tier (fee 500), then the coin's pool at 1%. Use exactInput with the whole path. The first leg can also be done off-pool: LTVault.mint(usdcIn, minSharesOut) mints the tracker at its NAV, with no slippage on that leg — which is how the launcher's own seed buy does it. It reverts while the vault is paused or its NAV is stale.
The coin is always token0 — it is CREATE2-mined to sort below both USDC and its tracker — so the side of a swap is known without reading the pool, and price is quoted the right way up by default.
Quote with QuoterV2 at 0x7dfd4f31be6814d2906bde155c3e1b146eac1468. Its quote functions are not declared view: they simulate the swap and unwind it, so call them with eth_call (or your library's simulate), not as a view and not in a transaction.
One warning from our own mistakes: chaining a multi-leg quote by calling the quoter once per leg gives nonsense, because each leg quotes against the current state rather than the state the previous leg would have left. Quote the whole path at once (quoteExactInput) or simulate on a fork.
The other canonical Uniswap addresses on Arc — factory, position manager, V4 — are listed in Architecture.
Discovering coins
The launcher is the registry. No indexer required:
MemeLauncherV3.allCoinsLength() → uint256
MemeLauncherV3.allCoins(uint256) → address
MemeLauncherV3.coinOf(address) → (quote, lt, oracle, creator, pool,
positionTokenId, tickLower, tickUpper,
graduationTargetUsdX18, graduated)
MemeLauncherV3.lockedLtPrincipal(coin) → the quote side of the locked position: the coin's backingquote tells you the stage: equal to MemeLauncherV3.usdc() means pre-bond, anything else means the coin is paired to its tracker. lt is filled in from the launch block either way — the launcher resolves the tracker at launch, because the coin's address has to sort below it. lockedLtPrincipal is in the quote's units: 6-decimal USDC before the bond, 18-decimal tracker after.
The thresholds are public too: bondBackingUsd() (15,000 USDC), minSeedUsdc() (1 USDC) and minRefPoolUsdc() (500 USDC).
Launching programmatically
launch(LaunchParams) is the one entry point. It pulls the seed buy (seedUsdcIn) plus any reference-pool seed (refPoolSeedUsdc) from the caller in USDC, so approve the launcher for that total first; it checks both balance and allowance before deploying anything, and fails with InsufficientSeedUsdc or SeedAllowanceMissing rather than a bare transfer error. The launcher takes no native value.
seedUsdcInmust be at leastminSeedUsdc()(1 USDC), or it revertsSeedTooSmall.leveragemust be between 1 andLTFactory.maxLeverageFor(underlyingSymbol), which is 0 for a market the factory has not listed.quoteInUsdc = trueis what the app sends: the coin trades against USDC and bonds later.falsequotes the coin in its tracker from the first block, and is refused (ReferencePoolTooThin) unless the tracker's USDC pool already holds at leastminRefPoolUsdc().navPostcarries a keeper-signed first NAV, used only when the launch creates the tracker; an empty signature skips it.- The creator is
msg.sender: the seed payer, the recipient of the seed coins and the owner of the fee stream.
Events
| Contract | Event | Meaning |
|---|---|---|
| launcher | Launched(coin, lt, pool, oracle, creator, positionTokenId, coinPriceUsdX18, graduationTargetUsdX18) | a coin and its pool exist |
| launcher | SeedBought(coin, creator, usdcIn, ltSpent, coinOut) | the creator's launch-block seed buy |
| launcher | Bonded(coin, lt, pool, positionTokenId, backingUsdc) | the coin re-paired to its tracker; the market moved to a new address |
| locker | FeesCollected(coin, caller, amount0, amount1) | swap fees swept out of the position |
| locker | CreatorFeesClaimed(coin, creator, amount0, amount1) | a creator withdrew their share |
| factory | SharedLtCreated(lt, oracle, underlyingSymbol, isShort, leverage) | a new tracker exists |
| factory | LtRoutingSet(lt, marketId, trader) | a tracker's edgeX market and hedge wallet |
| factory | Bonded(lt, depthUsd) | a tracker bonded: NAV now follows its oracle, the hedge is enabled |
| tracker vault | HedgeFunded(to, amount) | USDC sent to the hedge wallet, bound for edgeX |
| tracker vault | HedgeFunderUpdated(funder) | the hedge wallet changed; 0x0 is the kill switch |
| NAV oracle | NavPosted(nav, hedgeEquityUsd, underlyingPrice, timestamp) | a fresh signed NAV |
Bonded on the launcher is the one to handle carefully: the pool address changes, so a tape built on the old pool ends there and a new one begins. The coin is the stable identifier across both.
Indexing the tape
The pool is the tape. Watch its own Swap events and a trade placed by a bot you have never heard of, through a router you do not know, is indexed exactly like one placed on our own front. There is no proprietary event to decode and no zap to special-case.
Coins show up on DexScreener and GeckoTerminal under the arc network slug (https://dexscreener.com/arc/<pool>, https://www.geckoterminal.com/arc/pools/<pool>), keyed by the pool address — so after a bond, look the coin up again by its new pool.
Our own indexer is a subgraph, arc-perp on Goldsky, covering the launcher, the pools, the trackers and every HedgeFunded payment. It is built for Goldsky's arc-mainnet network, whose support for Arc mainnet subgraphs is still being confirmed with Goldsky; its endpoint is published with the deployment.
Reading a tracker's position
A tracker's position is not on Arc. It sits in an edgeX account held by the protocol's hedge wallet, because a contract cannot own an edgeX account. What Arc does record is the routing and every dollar that moves:
LTFactory.venueRouting(lt) → (marketId, trader) // edgeX contract id; trader = the hedge wallet
LTFactory.underlying(symbol) → (marketId, maxLeverage, listed)
LTVault.hedgeFunder() → the only address fundHedge can pay; 0x0 = cut off
LTVault.fundableUsdc() → what the vault could send it right now
LTVault.bonded() → false: NAV pinned at 1.0, nothing can leave for the venue
LTVault.targetLeverageX(), isShort(), leverageBandBps()
NAVOracle.peekNav() → (nav, updatedAt, stale), never reverts
NAVOracle.hedgeEquityUsd() → the account equity the keeper signed with the last NAVMoney in and out of edgeX passes through EdgexBridge at 0x61A7EBc69A34e72005a195B42b5d5577C20032aD on Arc: deposits appear as its Deposit events, and withdrawals arrive as USDC transfers into the tracker's vault, paid out by the bridge.
edgeX's public API serves every market with no key, at https://edgex-prod-v2.edgex.exchange:
/api/v2/public/meta/getMetaData— every market with its contract id, tick and step sizes, position caps, risk tiers and fee rates;/api/v2/public/quote/getTicker?contractId=30000020— last, index, oracle and mark prices, open interest, funding rate and next funding time;/api/v2/public/quote/getMarketStatus?contractId=30000020— whether the underlying market is open, and the limit band while it is not;/api/v2/public/funding/getLatestFundingRate?contractId=30000020.
A public WebSocket (wss://edgex-quote-prod-v2.edgex.exchange/api/v1/public/ws) streams tickers, depth, trades and funding. edgeX does not publish its rate limits; excess requests get HTTP 429. Account balances and positions are not public: they need that account's credentials. See The hedge book.
Permissions
Everything a trader, a creator or an integrator needs is permissionless: swap, quote, launch, read, bond a coin (bondAndMigrate) or a tracker (pokeBond), collect fees, claim protocol fees, mint and redeem trackers at NAV, execute the redemption queue, rebalance a tracker's reference pool, and relay a NAV the keeper has already signed. The restricted calls are:
claimCreatorFees— the creator of that coin;- the locker's
lock/migrateToLt— the launcher; - the factory's
getOrCreateSharedLt— an authorised launcher; its listing, routing and defaults — the factory owner; - a vault's
fundHedge— its owner or its hedge wallet, paying only the hedge wallet; - a vault's
setHedgeFunder,setPaused,setFeesand other settings — its owner; cancelRedeem— the holder who queued the redeem.