# Smart contracts

Rearctor creates a separate token, pool and fee vault for each reaction. A shared factory registers the launches; a shared hook enforces their canonical V4 pool settings. An optional dedicated protocol treasury accounts for the protocol’s fee receipts and manual spending.

**Deployment status:** Arc Testnet is connected to live Rearctor deployments; Mainnet remains a preview. The network selector separates the optimized Testnet deployment from the previous immutable deployment. See [Network and deployment status](/docs/network/) for exact identities. Existing Arc or Uniswap infrastructure addresses are not Rearctor factory, hook, vault or treasury addresses.

## Contract architecture

| Contract | Responsibility | Custody or authority |
| --- | --- | --- |
| `RearctorLaunchpad` | Validate launches, create and initialize clones, register the hook configuration, collect seed and optional dev-buy input | Owner sets up the hook, updates the minimum seed for future launches and may graduate eligible pools early; no per-launch fee or principal control |
| `RearctorToken` | Standard 18-decimal ERC-20; initial one-billion-token mint; holder burning | Initial supply goes to its pool; no public additional mint or transfer tax |
| `RearctorPool` | Curve trading, automatic or owner-directed migration, canonical V4 routing, block-opening price guard and locked liquidity | Holds curve assets, then owns the native V4 position; no principal withdrawal |
| `RearctorFeeVault` | Account for and distribute that project’s fees | Fixed beneficiaries; separate buyback and liquidity budgets |
| `RearctorHook` | Bind each canonical key and apply directional V4 fees | Initialization and LP additions restricted to its registered pool |
| `RearctorPriceGuard` | Block-opening execution reference and shared budget interval | Abstract implementation inherited by the pool; not a separate asset custodian |
| `RearctorTreasury` | Account for protocol receipts, operations, conversions and manual Rearc burns | Separate owner controls treasury spending |
| `RearctorHookDeployer` | Deploy a hook at a CREATE2 address with the required permission bits | Deployment helper; no later administrative control |

Token, pool and vault implementations are disabled for direct initialization. The factory creates minimal clones and initializes them within the launch transaction. Their implementation target is fixed; these are not upgradeable proxy deployments. The factory’s implementation, manager, quote and treasury addresses are fixed at construction; the hook is bound once before launches can proceed.

The older legacy implementation is not the architecture documented here. A production deployment must explicitly use the reviewed Rearctor contract family.

## Values and transaction conventions

Token amounts use 18 decimals; USDC quote amounts use 6. `deadline` is a Unix timestamp in seconds. Fee rates and creator-selected allocation shares are basis points, with 10,000 representing 100%. See [Tokenomics](/docs/tokenomics/) for the distinction between a trading fee and a share of collected fees.

Successful trades require a nonzero output minimum. Approve the correct input ERC-20 to the canonical pool before direct trading. A launch requires approval to the factory for seed plus any optional dev-buy input. Expired transactions revert, and a quote does not reserve the price or guarantee inclusion.

## Factory API

### Creation inputs

`createLaunch(LaunchParams p, FeeConfig fees)` returns `(token, pool, vault)`.

`LaunchParams` contains:

| Field | Meaning |
| --- | --- |
| `name`, `symbol` | Nonempty token name and symbol; maximum 64 and 12 UTF-8 bytes |
| `description` | Up to 1,024 bytes |
| `image` | Up to 512 bytes |
| `website`, `socialX`, `telegram` | Up to 256 bytes each |
| `seed` | At least the current owner-set minimum (1 USDC initially), strictly below 5,042 USDC |
| `devBuy` | Optional USDC purchase input; zero disables the purchase |
| `devBuyMinOut` | Minimum token output for a nonzero dev buy |
| `deadline` | Latest allowed block timestamp for creation and its purchase |

Contract metadata checks enforce byte lengths, not verified ownership of a name, website or social account. Names and symbols can be duplicated. The application may apply stricter formatting than the contract.

`FeeConfig` contains `buyBps`, `sellBps`, `creatorBps`, `buybackBps`, `liquidityBps` and `creatorWallet`. Each directional fee is 100–1,000 bps. The three allocation fields total 10,000 bps and apply to the 70% remaining after the protocol allocation. The creator wallet must not be a rejected system address.

### Reads and setup

| Method | Semantics |
| --- | --- |
| `launchCount()` | Number of registered launches |
| `getLaunches(offset, limit)` | Returns metadata and immutable fee settings; limit is capped at 100 |
| `isPool(pool)` | Canonical factory registration check |
| `isSystemAddress(account)` | Addresses rejected as creator beneficiaries |
| `previewLaunchBuy(seed, amount, buyBps)` | Hypothetical curve purchase at initialization; returns output, fee and actual spend |
| `previewDevBuy(seed, amount, buyBps)` | Same preview, additionally enforcing the 20% token cap and the requirement to leave the curve open |
| `initializeHook(hook)` | Owner-only, one-time binding; validates the factory and manager relationship |
| `quoteToken()`, `treasury()`, `manager()`, `hook()` | Deployment identity and routing configuration |
| `minimumSeed()`, `graduationThreshold()`, `minimumFeeBps()`, `maximumFeeBps()`, `maximumDevBuyTokens()` | Current minimum seed and fixed economic limits exposed on-chain |

An owner can transfer the factory owner role through the inherited two-step ownership flow. Renouncing ownership is disabled. After hook setup, that owner cannot change a launch’s fees, replace its hook or withdraw its principal. On updated pools it can request early graduation, subject to the pool’s eligibility, deposit and dead-transfer limits.

## Pool API

### Trading and quoting

| Method | Semantics |
| --- | --- |
| `buy(amount, minOut, deadline)` | Buy with USDC; uses the curve before graduation and the stored canonical V4 key afterward |
| `sell(amount, minOut, deadline)` | Sell project tokens through the corresponding current stage |
| `quoteBuy(amount)` | Curve-only output and USDC fee |
| `previewBuy(amount)` | Curve-only output, fee and capped actual USDC spend |
| `maxCurveBuy()` | Gross USDC input required to reach the curve target |
| `quoteSell(amount)` | Curve-only USDC output and fee; returns zero if real USDC backing is insufficient |
| `quoteBuyback(amount)` | Curve-stage fee-free project buyback preview; does not establish price-guard eligibility |
| `getPoolKey()`, `poolId()` | The registered V4 key and its ID |
| `sqrtPriceX96()` | V4 square-root price in currency1/currency0 base-unit orientation |

`quoteBuy`, `previewBuy`, `maxCurveBuy`, `quoteSell` and `quoteBuyback` return zero once graduated. This is not a trading shutdown. Use the matching V4 Quoter and `getPoolKey()` after graduation.

For a V4 exact-input quote, use `quoteExactInputSingle` with the canonical key, input direction determined by its currency ordering, the amount in input base units and empty hook data. Verify that the Quoter’s `poolManager()` matches the pool’s manager. The application uses amounts below `2^127` for the signed V4 input-delta path.

Do not choose a quote path solely from an indexed `graduated` value. The application reads fresh canonical state at a pinned block, validates the pair, vault, manager and hook, and requotes after approvals. It never lowers the originally accepted absolute minimum. The contract makes the final routing decision using its state at execution.

### Fees, budgets and guard reads

| Method | Caller and effect |
| --- | --- |
| `collectV4Fees()` | Anyone, after graduation; realizes earned LP fees into the project vault without removing principal |
| `claimFees()` | Claims the caller’s vault entitlements; does not withdraw pool reserves |
| `executionPriceState()` | Returns opening tick and current spot tick; historical TWAP storage is absent in the latest revision |
| `maxBudgetInput(buySide)` | Maximum input for a guarded swap: 0.5% of the relevant real or locked-position input reserve |
| `guardedMinimum(buySide, amount, feeFree)` | Calculates the guard floor for valid, bounded input after the block-opening deviation checks |
| `buyWithPriceGuard(...)`, `sellWithPriceGuard(...)` | Treasury-only guarded variants; the caller’s minimum can only tighten the guard floor |
| `previewFeeLiquidity(tokenAmount, quoteAmount)` | Estimates the quantities a V4 LP addition can use; not an execution guarantee |
| `previewLiquidityRebalance(tokenAmount, quoteAmount)` | Computes direction and a bounded excess-side amount at the guarded block-opening price |

`executeBuyback`, `rebalanceFeeLiquidity` and `addFeeLiquidity` are vault-only execution paths. `buyFromFactory` is factory-only. `recordPrice` is hook-only. `unlockCallback` is PoolManager-only and interprets the pool’s internally generated actions; it is not an arbitrary public router.

The `graduated` flag is permanent. After migration, `tokenReserve` and `quoteReserve` are cleared because custody has moved to V4. They are not a measure of V4 liquidity. Read the canonical V4 state alongside `lockedLiquidity`, `migratedToken`, `migratedQuote`, the locked remainder counters and `deadTokenRemainder`. The last field records the manual-graduation transfer; it is not a reduction in token `totalSupply`.

### Manual graduation interface

| Function | Meaning |
| --- | --- |
| `manualGraduationEnabled()` | Manual-graduation capability marker; older versions may lack the dead-transfer extension |
| `DEAD_ADDRESS()` | Fixed destination for the manual-graduation token remainder |
| `deadTokenRemainder()` | Accounted tokens sent to dead at manual graduation; excludes other transfers to dead |
| `previewGraduation()` | `(usedToken, usedQuote, remainderToken, remainderQuote, eligible)`; on the manual path the token remainder is sent to dead and the quote remainder stays locked |
| `manualGraduate(minToken, minQuote, maxDeadTokens, deadline)` | Current factory owner only; next-block restriction; early reserves only; at least 200 million tokens and 99% of accounted USDC deposited; token transfer to dead must not exceed the caller maximum |

The caller cannot redirect assets or change fees. Only the accounted token remainder is transferred to `0x000000000000000000000000000000000000dEaD`; donations, fee budgets and V4 deposits are excluded. This transfer does not invoke `burn` or reduce `totalSupply`. Any quote remainder stays locked. Automatic migration retains its small remainders in the pool.

Minimum deposits must be positive; `maxDeadTokens` may be zero to forbid any nonzero remainder transfer. Deposit shortfalls, a remainder above this cap or an expired deadline revert the whole migration. Accepted factory ownership transfers change this authority. Old immutable clones require a new deployment to support this method and its getters; `manualGraduationEnabled()` alone does not identify the extension.

## Fee vault API

| Method or read | Semantics |
| --- | --- |
| `claimFor(recipient)` | Anyone can trigger both-asset claims; funds go only to the specified beneficiary’s accrued entitlement |
| `processBuyback(minOut, deadline)` | Anyone can process the available project budget, with the pool clipping USDC purchases to its guard/curve limits; acquired and already budgeted project tokens are burned |
| `processLiquidity(minToken, minQuote, deadline)` | Anyone, after graduation; reinvest available paired budgets into the same locked position |
| `rebalanceLiquidity(minOut, deadline)` | Anyone, after graduation; swap a calculated portion of the excess liquidity asset through the canonical pool |
| `totalAccrued(asset)` | Cumulative fees recognized for this asset |
| `allocated(asset, bucket)` | Cumulative entitlement; bucket 0 protocol, 1 creator, 2 buyback, 3 liquidity |
| `claimable(asset, beneficiary)` | Beneficiary’s currently unclaimed fee balance |
| `protocolClaimed(asset)` | Cumulative protocol-only receipts used by the treasury |
| `buybackBudget(asset)`, `liquidityBudget(asset)` | Unspent balances of the separate project budgets |
| `totalBuybackQuoteSpent()`, `totalBuybackTokensBurned()`, `buybackExecutions()` | This vault’s cumulative buyback counters |
| `distributionHash()` | Binding of the project addresses, beneficiaries and allocation configuration |

Only the registered pool can call `accrue`, and only for the configured quote or token asset. Direct transfers to the vault are not automatically recognized as fees or spendable budgets. There is no general vault rescue function that turns unaccounted donations into withdrawable revenue.

No administrator is needed to call the three project-budget processing methods. Permissionless callers cannot choose their own destination or take the output. Processing can still fail because of an uninitialized price reference, the price guard, unbalanced amounts, output minima or token transfer restrictions.

## Hook and canonical identity

The hook’s required address flags are `0x2880`: `beforeInitialize`, `beforeAddLiquidity` and `beforeSwap`. Its key uses the sorted token/quote addresses, dynamic-fee flag `0x800000`, tick spacing 200 and the hook itself.

Only the factory can `register` a configuration, and an existing pool ID cannot be overwritten. Registration cross-checks the pool, vault and distribution hash. Only PoolManager can invoke the hook callbacks. Initialization and LP additions additionally require the registered RearctorPool as sender. Swaps through this key may come through other routers, but they still invoke the hook and its directional fee logic.

Canonical validation should start from a verified factory deployment and confirm `isPool`, the pool’s `factory`, `token`, `quote`, `vault`, `manager`, `hook`, full `getPoolKey()` and `poolId()`. A familiar symbol or a token pair alone is not sufficient.

## Protocol treasury API and permissions

The protocol treasury has its own two-step owner role, separate from the factory’s setup role. Renouncing that role is disabled.

| Method | Permission and meaning |
| --- | --- |
| `bindFactory(factory)` | Owner, once; validates the quote, treasury destination and compatible vault implementation |
| `bindRearctorPool(pool)` | Owner, once; selects a canonical pool belonging to the bound factory as the permanent buyback target |
| `collectProtocolFees(vaults)` | Anyone; validates and collects 1–50 canonical sources into the treasury |
| `convertProtocolTokens(pool, amount, minUSDC, deadline)` | Owner; sell accounted protocol token receipts through their own canonical guarded pool |
| `executeRearctorBuyback(amount, minTokens, deadline)` | Owner; spend up to the supplied USDC amount through the fixed target, then burn only the newly acquired tokens |
| `withdrawOperations(amount, recipient)` | Owner; withdraw accounted protocol USDC for operations |
| `withdrawProtocolTokens(asset, recipient, amount)` | Owner; withdraw accounted protocol token receipts without relying on a working conversion market |
| `recoverSurplus(asset, recipient, amount)` | Owner; recover balance above the recorded protocol liabilities, such as donations |
| `onProtocolFeesReceived(asset)` | Authenticated canonical-vault accounting callback; not an open income-crediting function |

The contract’s target binding validates a canonical pool; it does **not** enforce the name “Rearc,” the symbol, or launch index zero. The application’s official-token verification additionally checks the first registered launch and its treasury binding. Verify both layers before treating a token as official.

A curve-stage treasury purchase can spend less than requested when it completes graduation. The current treasury records **actual USDC spent**, preserves the refund and burns only the newly purchased tokens. It also verifies the corresponding supply reduction. The requested maximum must still fit the treasury’s available recorded USDC and applicable pool guard checks.

Useful accounting reads include `availableProtocolUSDC`, `totalProtocolUSDCReceived`, `totalUSDCFromConversions`, `totalOperationsUSDCWithdrawn`, `totalBuybackUSDCSpent`, `totalRearctorBurned`, `buybackExecutions` and the per-token received, available, converted and withdrawn counters.

## Events and indexing semantics

| Emitter / event | What it establishes |
| --- | --- |
| Factory `LaunchCreated(id, token, pool, creator, vault)` | Canonical launch identities and directory ID |
| Factory `DevBuy(token, creator, tokensBought)` | The optional creation-time purchase |
| Pool `Trade(trader, buy, amountIn, amountOut, fee)` | Router-level execution; curve fees are USDC, V4 `fee` is a configured estimate in the input asset |
| Pool `Graduated(id, tokenAmount, quoteAmount, manual)` | Canonical V4 migration; automatic migration emits `manual = false`; owner-directed early migration emits `manual = true` |
| Pool `GraduationRemainderSentToDead(address indexed recipient, uint256 amount)` | Accounted token remainder transferred to the fixed dead address during manual graduation; total supply unchanged; separate from fee burns |
| Pool `V4FeesCollected(tokenAmount, quoteAmount)` | LP fees transferred to the vault for allocation |
| Hook `PoolRegistered(...)` | Pool ID, related contracts, immutable directional rates and distribution binding |
| Vault `FeesAccrued`, `Claimed`, `ProtocolClaimed` | Fee recognition, beneficiary payment and protocol-only receipt accounting |
| Vault `Buyback(quoteSpent, tokensBurned)` | Project-budget execution; distinct from protocol-funded Rearc burns |
| Vault `LiquidityRebalanced`, `LiquidityReinvested` | Budget conversion and locked-position additions |
| Treasury `FactoryBound`, `RearctorPoolBound` | One-time source and target bindings |
| Treasury `ProtocolFeesReceived`, `ProtocolTokensConverted` | Recorded protocol receipts and conversions |
| Treasury `OperationsWithdrawn`, `ProtocolTokensWithdrawn`, `SurplusRecovered` | Owner-directed treasury outflows |
| Treasury `ProtocolBuyback(quoteSpent, tokensBurned)` | The treasury’s manual Rearc purchase and burn |

Use PoolManager’s **`Swap`** events filtered by the full canonical pool ID for complete V4 trade history and actual V4 fee fields. A pool’s post-graduation `Trade` event duplicates the corresponding swap when the pool acts as router; counting both doubles volume. External-router swaps can produce a canonical V4 `Swap` without a Rearctor `Trade`.

Similarly, the pool’s `volume` counter tracks its buy/sell routing paths and is not a complete count of all external-router V4 activity. Indexers need a clear graduation boundary, transaction/log identity, confirmed block coverage and reorganization handling. An external-router `Swap.sender` can be a router contract, not the ultimate wallet. Holder burns emit the ERC-20 burn transfer but do not increase a vault’s or treasury’s buyback counters.

## Permission summary

Users control their own trades and token burns. Anyone can create a valid launch, claim for a fixed beneficiary, collect V4 fees, execute eligible project budgets and collect protocol receipts. The factory owner establishes the initial hook, can transfer its role and can execute eligible early migration through `manualGraduate`. The treasury owner retains spending authority over treasury assets. No role can withdraw a project pool’s committed principal, rewrite its fee split, replace its canonical hook, mint extra project supply.

See [Permissions and liquidity](/docs/security/) for contract roles and asset controls.

### Adjustable minimum initial liquidity

`minimumSeed()` starts at 1 USDC (`1_000_000` base units). Only the current factory owner may call `setMinimumSeed(uint256)`. The amount must be at least `minimumSeedFloor()` (1 USDC in the next deployment; the existing Testnet revision permits one base unit, 0.000001 USDC) and strictly below 5,042 USDC. `MinimumSeedUpdated(previousMinimum,newMinimum)` records each update. The deposit remains pool principal; this setting is not a protocol fee. Existing pools, fees, reserves and graduation thresholds are unaffected. Both launch previews use the current setting.
