rail docs
How rail works
Plain-language explanations of launching, trading, graduation and fees, followed by an integration reference for builders. Numbers on this page are read live from the deployed contracts.
What rail is
rail is a noncustodial token launchpad on Arc, Circle's USDC-native chain. Anyone can launch a token in one transaction. It trades on its own bonding curve until $10,000 has been raised, then graduates into a Uniswap v4 pool whose liquidity is locked forever.
- USDC is everything. It pays gas on Arc, prices every token, and is the only currency fees are charged in. There is no second gas token to hold.
- Your wallet signs everything. rail never holds your keys or your funds. Contracts move tokens only in the transactions you sign.
- Launch terms are frozen. Once a token exists, its fees, recipient and curve can't be changed by anyone, including the rail team.
Showing default parameters; live values load from chain when available.
Getting started
Add the Arc network
Most wallets add the network automatically when you connect. To add it by hand:
| Field | Value |
|---|---|
| Network name | Arc |
| Chain ID | 5042 |
| RPC URL | https://railpad.io/api/rpc |
| Currency symbol | USDC |
| Block explorer | https://arc-scan.org |
Get USDC
You need USDC on Arc for gas and for buying. Your wallet may show the native balance with 18 decimals; rail reads the USDC token (6 decimals). It's the same balance, so you only ever see one USDC number.
What one-click trading does
Buying or selling needs permission for the rail contract to move your tokens. When you press Buy, rail picks the shortest route your wallet supports:
- Already approved: one transaction.
- Batching wallet (EIP-5792): approval and trade in one prompt.
- Permit (EIP-2612): you sign a free, off-chain message granting the exact amount, then send one transaction. USDC and every rail token support permits.
- Fallback: an approve transaction, then the trade, automatically.
The caption under the button tells you which route will be used. "Unlimited approval" (off by default) approves the maximum so later trades need a single prompt.
Launching a token
Create deploys the token and its bonding curve in one transaction. Every launch gets 1B tokens, all held by the curve. Here is every field.
Name, symbol, image, description
These are labels. They are not unique: anyone can launch another token called the same thing with the same picture. The contract address is the only identity that matters, so always check it before buying. Images are pinned to IPFS.
Creator tax
An extra percentage on every buy and sell, on top of the 1% trade fee, between 0% and 10%. The default is 1%. Where it goes depends on the buyback mode below. It is fixed forever at launch.
Buyback mode: Off, Burn or Holder rewards
| Mode | Fee share + creator tax go to | Creator earns |
|---|---|---|
| Off (default) | The fee recipient, via the escrow | Fee share + tax |
| Burn | Buying the token back from the market and burning it | Nothing |
| Holder rewards | Holders, paid in USDC, pro-rata to their balance | Nothing |
The mode (Burn or Holder rewards) is chosen at launch and never changes. You can launch with buyback off and turn it on later from your profile or the token page.
Fee recipient
The address that receives creator earnings. It defaults to your wallet and is permanent: there is no transfer and no takeover. Use an address you will control for the life of the token. While buyback is on it receives nothing.
Early-buy wallets
The first 5 seconds carry a steep snipe tax. You can list up to 32 wallets that skip it, for your own opening buys. You and the fee recipient are always exempt.
Opening buy
An optional amount of USDC to buy in the same transaction. It is atomic (nobody can buy before you) and untaxed by the snipe tax. Normal trade fees still apply.
Launch fee
Each launch costs 1 USDC, pulled together with the opening buy. It goes to the protocol.
The bonding curve
Think of the curve as a vending machine that holds every token and a pile of USDC. Put USDC in, get tokens out; put tokens back, get USDC out. The price is simply the ratio of what's inside, so there is always someone to trade with.
- Starting price. The machine starts with a virtual $4,000 of USDC, so a new token opens at a $4,000 market cap. No real money sits behind the virtual part.
- Buys raise the price, sells lower it. Every token bought leaves fewer inside, so the next one costs more.
- You can always sell back until the curve sells out; the machine never runs out of the USDC buyers put in.
- Size moves price. A large order walks up the curve and gets a worse average price than a small one.
- Slippage is rate-based. Your minimum output is checked against the price you actually got, so a refunded remainder never trips it.
- The finishing buy is clamped. If a buy would take more tokens than remain for sale, it gets exactly the rest and the unused USDC is refunded in the same transaction.
28.57% of supply is never sold on the curve: it is reserved for the pool at graduation. The curve sells out once $10,000 of real USDC has come in, at ~$49,000 market cap.
Snipe protection
Bots try to buy in the same block a token launches. To make that unprofitable, buys in the first 5 seconds pay a tax that starts at 99% and falls to a quarter each second. Sells are never snipe-taxed. The tax is added to the trade fee.
| Seconds after launch | Snipe tax |
|---|---|
| 0 | 99% (9900 bps) |
| 1 | 24.75% (2475 bps) |
| 2 | 6.18% (618 bps) |
| 3 | 1.54% (154 bps) |
| 4 | 0.38% (38 bps) |
| 5+ | 0% (0 bps) |
Exempt: the launcher, the fee recipient and up to 32 wallets listed at launch. The trade panel shows the live snipe tax when it applies.
Graduation
When the curve sells out ($10,000 raised), the token graduates in that same finishing buy:
- The raised USDC and the reserved 28.57% of supply are swept out of the curve.
- A Uniswap v4 pool is created at the curve's final price. Every graduated pool starts with the same $10,000 of USDC.
- The liquidity is minted as one full-range position owned by the locker contract, which has no withdraw function. It is locked forever.
- Reserved tokens not needed to match the price are burned.
If pool creation can't complete inside the finishing buy (for example, too little gas), the buy still succeeds and the launch waits in a "graduating" state. Anyone can retry, and the rail keeper retries automatically within seconds.
Fees
Every trade pays a 1% base fee plus the token's creator tax (0–10%), plus the snipe tax in the first seconds.
Who gets what
| Protocol | Fee recipient | Burns / holders | |
|---|---|---|---|
| Buyback off | 30% of base fee | 70% of base fee + creator tax | — |
| Burn | 30% of base fee | 0 | 70% of base fee + creator tax buys & burns the token |
| Holder rewards | 30% of base fee | 0 | 70% of base fee + creator tax paid to holders in USDC |
The snipe tax is added to the base fee and split the same way.
Rules
- Same rate everywhere. The curve and the graduated pool charge identical rates, in every direction.
- USDC only. Fees are always taken in USDC, never in the token.
- Rounded up. Fee amounts round up to the next smallest unit, so the protocol never undercharges by rounding.
- Pool trades fill completely or revert. If the pool can't take your whole input (price limit reached), the trade reverts and no fee is charged. Try a smaller amount.
Sweeping and claiming
Fees accrue on the curve or pool. A sweep splits them and deposits each share into the fee escrow. Sweeps that don't need a market buy (buyback off, or Holder rewards) are permissionless: anyone can press "Distribute fees". Burn-mode sweeps are run by the keeper.
Recipients claim from the escrow on their profile under Fees. Claimed funds go straight to the wallet.
Holder rewards
In Holder rewards mode, each sweep sends the fee share and tax to the token contract as USDC and credits every eligible holder pro-rata to their balance at that moment. Rewards already credited to you stay yours even if you sell afterwards. Claim anytime from the token page or your profile; there is no expiry.
Not eligible (their balances would dilute real holders): the zero and dead addresses, the token itself, its curve, the factory, the launch deployer, the locker, the v4 PoolManager, the hook, the fee escrow and the protocol fee router. These are fixed when the token is created. Third-party AMM pairs are not excluded.
Sweep cadence. Anyone can sweep; the keeper sweeps rewards launches when accrued rewards pass a threshold or age, keeping each payout small.
If no eligible holders exist yet, the reward is carried forward to the next sweep, never paid to the creator.
Burn mode
In Burn mode the fee share and tax buy the token on the market and burn it, permanently reducing supply. Because a market buy can be sandwiched, only approved sweep operators (the rail keeper) can run these sweeps, with a minimum-output quote.
- On the pool, each buyback is capped at 3% price impact.
- Whatever can't be spent (impact cap reached, no liquidity) stays as pending buyback and is retried next sweep. It never goes back to the creator.
- Buybacks on the curve pay no fee, and never buy the last tokens that would trigger graduation.
RAIL and protocol buy & burn
The protocol's share of fees goes to the ProtocolFeeRouter. When it claims from the escrow, 80% of each USDC amount enters a burn budget and 20% goes to the treasury. Operators spend the budget buying RAIL through the rail router, in impact-bounded chunks, and burn it.
RAIL is an ordinary rail launch made by the team. The owner can point the burn at a different launch at any time.
Safety and trust
Immutable
- Token contracts, bonding curves, the Uniswap v4 hook, the liquidity locker and the fee escrow (no proxies, no admin upgrade).
- The terms of a live launch: supply, fee rate, creator tax, economics, snipe settings, buyback mode.
- The fee recipient of every launch.
- Buyback once enabled.
What the owner can do
- Pause new launches or restrict them to a whitelist.
- Set launch configs, pair-token economics, launch fee, max creator tax, snipe config and fee policy: these apply to future launches only.
- Approve pair tokens and add or remove sweep operators.
- Rescue a stuck graduation after 7 days.
- Change ProtocolFeeRouter settings (burn share, treasury, operators, RAIL target) and upgrade it.
What nobody can do
- Mint tokens, freeze or blacklist holders, or add transfer taxes.
- Withdraw or move locked pool liquidity.
- Change a live launch's fees, terms or fee recipient.
- Turn buyback off.
Risk disclosures
- Tokens are launched by anyone. Most go to zero. Nothing here is investment advice.
- Names, tickers and images can be copied. Verify the contract address.
- Smart contracts can contain bugs. The core is immutable, so a bug can't be patched in place.
- Prices move fast, especially in the first minutes; slippage and the snipe tax can make early buys expensive.
- Holder rewards and burns depend on trading volume and can be zero.
- Arc, USDC, wallets and RPC providers are third-party dependencies outside rail's control.
- Transactions are final.
FAQ
Why did my pool trade revert?
The pool couldn't fill your whole order within its price range (PartialFillNotSupported), or the price moved past your slippage. No fee was charged. Try a smaller amount or a higher slippage.
Why is the snipe tax on?
The token launched less than 5 seconds ago. Wait a few seconds and it drops to zero.
Why do I see a signature request?
That's a permit: a free, off-chain approval for the exact amount you're trading. It replaces a separate approve transaction. Decline it and click again to use a normal approval.
Where are my creator fees?
Fees sit on the curve or pool until swept, then in the escrow. Open your profile → Fees: press "Distribute fees" on your launch, then "Claim". With buyback on, creators earn nothing.
Why is my rewards balance 0?
The token isn't in Holder rewards mode, buyback is off, no sweep has happened since you bought, or your address is excluded (for example, you hold through a contract that's excluded).
What happens if graduation fails?
Your trade still settles. The launch waits as "graduating" while the keeper retries; anyone can retry too. After 7 days the owner can rescue it (USDC returned to a chosen recipient, tokens burned).
Integration
Contracts
Active network: Arc (chain id 5042). Launch tokens have 18 decimals; USDC has 6. ABIs are generated from the interfaces in contracts/src/interfaces.
| Contract | Address | Role |
|---|---|---|
| factory | 0xF2BBe838C15da206A33Ce2fE8AE833c858c431AD | Launches tokens, stores launch records and config, graduation and rescue |
| router | 0xe0BCAE6cA3b9d1c2626Ea6008B9B5cB098F29062 | Stateless trading entry point: routes buys/sells to curve or pool, quotes, permits |
| hook | 0xc3d1721Da76F075977a8FD098be4d542f3B720CC | Uniswap v4 hook: fees, pending fees/tax/buyback per pool, pool sweeps |
| escrow | 0x928D49FCb22f4a3c981421e02Deb0e2aA419eF51 | Holds swept fees per recipient until claimed |
| locker | 0xC11f5F57a750477bcE7b4461729667Be888067c7 | Owns graduated full-range positions forever (no withdraw) |
| deployer | 0xa0345861Dc8ef4C5FC41F7422cD13B089b099a98 | CREATE2 deployer for tokens and curves |
| feeRouter | 0x3e8886cad58218eD3985EF0A6921849860ccfA23 | ProtocolFeeRouter (UUPS proxy): claims protocol fees, RAIL buy & burn |
| poolManager | 0x8366a39CC670B4001A1121B8F6A443A643e40951 | Uniswap v4 PoolManager |
| usdc | 0x3600000000000000000000000000000000000000 | USDC (6 decimals) |
| stateView | 0xF3334192D15450CdD385c8B70e03f9A6bD9E673b | Uniswap v4 StateView |
Launching programmatically
Pin the economics you expect with previewLaunchEconomics(configId, pairToken): if the owner changes the config or pair economics before your transaction lands, the launch reverts with LaunchEconomicsMismatch instead of launching on terms you didn't see.
import { parseUnits, toHex } from "viem";
import { factoryAbi } from "./abis";
const configId = 0n;
const expectedEconomics = await publicClient.readContract({
address: FACTORY, abi: factoryAbi, functionName: "previewLaunchEconomics", args: [configId, USDC],
});
const params = {
name: "Arc Rider", symbol: "RIDE", logo: "ipfs://…", description: "",
socials: { twitter: "", telegram: "", discord: "", website: "", farcaster: "" },
creatorFeeRecipient: account.address, // permanent
creatorTaxBps: 100, // <= maxCreatorTaxBps (1000)
buybackEnabled: false,
buybackMode: 0, // 0 Burn, 1 HolderRewards (fixed forever)
expectedEconomics,
salt: toHex(crypto.getRandomValues(new Uint8Array(32))), // namespaced: keccak256(abi.encode(sender, salt))
};
const quoteIn = parseUnits("25", 6); // optional atomic opening buy (0 for none)
const minTokensOut = 0n; // rate-based; compute from the curve formulas below
const exemptions: `0x${string}`[] = []; // up to 32 early-buy wallets
// approve FACTORY for launchFee + quoteIn (same token), then:
const { request } = await publicClient.simulateContract({
account, address: FACTORY, abi: factoryAbi, functionName: "launchToken",
args: [params, configId, USDC, exemptions, quoteIn, minTokensOut],
});
const hash = await walletClient.writeContract(request);
// parse TokenLaunched(token, curve, deployer, …) from the receiptlaunchTokenWithPermit(…, permit) takes an extra PermitData {value, deadline, v, r, s} for an EIP-2612 permit on launchFeeToken (owner = sender, spender = factory) covering launch fee + quoteIn. With quoteIn > 0 the pair token must equal the launch-fee token. The permit is try/catch'ed, so a front-run permit doesn't brick the call as long as the allowance exists.
const nonce = await publicClient.readContract({ address: USDC, abi: erc20PermitAbi, functionName: "nonces", args: [owner] });
const deadline = BigInt(Math.floor(Date.now() / 1000) + 1200);
const sig = await walletClient.signTypedData({
account, primaryType: "Permit",
domain: { name: "USDC", version: "2", chainId: 5042, verifyingContract: USDC }, // launch tokens: name = token name, version "1"
types: { Permit: [
{ name: "owner", type: "address" }, { name: "spender", type: "address" },
{ name: "value", type: "uint256" }, { name: "nonce", type: "uint256" }, { name: "deadline", type: "uint256" },
] },
message: { owner, spender: FACTORY, value, nonce, deadline },
});
const { v, r, s } = parseSignature(sig);
const permit = { value, deadline, v: Number(v), r, s };The launcher and the fee recipient are always snipe-exempt, so the opening buy pays no snipe tax; it still pays the normal 1% fee + creator tax. exemptions add more early-buy wallets.
Trading
The router routes by factory phase: 0 → bonding curve, 2 → Uniswap v4 pool, anything else → NotTradable. It has no privileges and pulls the pair token (buys) or launch token (sells) from the caller.
function buy(address token, uint256 quoteIn, uint256 minTokensOut, address recipient, uint256 deadline) returns (uint256 tokensOut);
function sell(address token, uint256 tokensIn, uint256 minQuoteOut, address recipient, uint256 deadline) returns (uint256 quoteOut);
function buyWithPermit(address token, uint256 quoteIn, uint256 minTokensOut, address recipient, uint256 deadline, PermitData permit);
function sellWithPermit(address token, uint256 tokensIn, uint256 minQuoteOut, address recipient, uint256 deadline, PermitData permit);- Curve refunds (finishing buy clamp) are forwarded to the caller.
buyWithPermitpermits the pair token;sellWithPermitpermits the launch token (domain name = token name, version "1").- You may also call the curve directly while in phase 0:
curve.buy(quoteIn, minTokensOut, recipient)/curve.sell(tokensIn, minQuoteOut, recipient)(approve the curve).
Quoting
router.quoteBuy(token, quoteIn, recipient) → (tokensOut, spent) and router.quoteSell(token, tokensIn) → quoteOut work in both phases. They are non-view (the pool path simulates a swap), so call them with eth_call / simulateContract.
Curve math, integer order is normative (BPS = 10,000; fees round up):
amountOut(in, rIn, rOut) = in * rOut / (rIn + in)
amountIn(out, rIn, rOut) = out * rIn / (rOut - out) + 1
sellable = tokenReserve - reservedTokens
reservedTokens = supply * phantom / (phantom + threshold)
// buy(quoteIn)
snipeBps = min(currentSnipeTaxBps(recipient), BPS - feeBps - creatorTaxBps - 100) // if > 0
fee = ceilDiv(quoteIn * feeBps, BPS)
tax = ceilDiv(quoteIn * creatorTaxBps, BPS)
snipe = ceilDiv(quoteIn * snipeBps, BPS)
net = quoteIn - fee - tax - snipe
tokensOut = amountOut(net, quoteReserve, tokenReserve)
if tokensOut > sellable: // finishing buy: clamp + refund
tokensOut = sellable
netNeeded = amountIn(sellable, quoteReserve, tokenReserve)
spent = min(quoteIn, smallest s with s - fees(s) >= netNeeded)
require tokensOut * quoteIn >= minTokensOut * spent // rate-based slippage
// sell(tokensIn)
gross = amountOut(tokensIn, tokenReserve, quoteReserve)
out = gross - ceilDiv(gross * feeBps, BPS) - ceilDiv(gross * creatorTaxBps, BPS)curve.currentSnipeTaxBps(recipient)returns the live snipe tax (0 for exempt recipients). Defaults: start 9900 bps, window 5s,start >> (2·elapsed).- Stop quoting the curve when
sellableTokens() == 0orreadyToGraduate(); route to the pool once phase is 2. - Pool trades revert with
PartialFillNotSupportedif the pool can't fill an exact-in order; quote first and size accordingly.
Reading state
factory.getLaunchedToken(token) returns the full launch record: curve, deployer, creatorFeeRecipient, pairToken, supply, economics, fee and tax bps, buybackEnabled, buybackMode, phase, swept amounts, poolId, launchedAt, exists.
| phase | Meaning |
|---|---|
| 0 | NotGraduated: trading on the curve |
| 1 | Swept: curve sold out, pool not created yet (retryable) |
| 2 | PoolCreated: trading on Uniswap v4 |
| 3 | Rescued: stuck launch rescued by the owner |
- Curve:
getReserves(),realQuoteReserve()(progress = realQuoteReserve / graduationThreshold),sellableTokens(),quoteFeeBalance(),creatorTaxBalance(),pendingBuyback(). - Hook:
pendingFees(poolId),pendingCreatorTax(poolId),pendingBuyback(poolId). - Token rewards:
claimableRewards(holder),claimedRewards(holder),totalRewardsDistributed(),eligibleSupply(),isExcludedFromRewards(account).
Fees and claiming
- Escrow:
balanceOfToken(recipient, asset),claimToken(asset),claimTokens(assets[]). Funds go tomsg.sender. - Sweeps:
curve.sweepFees(minBuybackTokensOut)andhook.sweepPoolFees(poolId, minBuybackTokensOut). Permissionless when buyback is off or mode is HolderRewards (pass 0). Burn mode with a buyback share requires a sweep operator (InternalSwapRequiresOperator). - Rewards:
token.claimRewards()sends the caller's accrued USDC. - Enable buyback (one-way):
factory.enableBuyback(token), callable by the deployer or fee recipient.
Events to index
| Contract | Event | Meaning |
|---|---|---|
| Factory | TokenLaunched / LaunchDetails | New launch; details carries metadata and terms |
| Factory | LaunchSwept / PoolGraduated / GraduationExcessBurned | Graduation steps |
| Factory | LaunchRescued | Stuck launch rescued |
| Factory | BuybackEnabled | One-way buyback switch |
| Factory | LaunchConfigSet / PairTokenSet / FeePolicySet / SnipeConfigSet / LaunchFeeSet / … | Owner config for future launches |
| Curve | CurveBuy / CurveSell / CurveBuyRefunded | Trades with post-trade reserves |
| Curve | CurveCompleted / AutoGraduationFailed | Sold out / graduation needs a retry |
| Curve | FeesSwept / BuybackBurned / HolderRewardsPaid / BuybackDeferred / BuybackForwardedToPool | Sweep outcomes |
| Hook | PoolRegistered / PoolSwap | Pool registered; swaps with swapper-facing amounts (trader = tx.from) |
| Hook | PoolFeesSwept / PoolBuybackBurned / PoolHolderRewardsPaid / PoolBuybackDeferred / PoolBuybackCredited | Pool sweep outcomes |
| Token | Transfer / RewardsDistributed / RewardsClaimed | Balances, burns (to 0x0), rewards |
| Escrow | CreditedToken / ClaimedToken | Fee credits and claims |
| Locker | PositionLocked | Liquidity locked at graduation |
| FeeRouter | Claimed / BoughtAndBurned / LaunchTokensBurned | Protocol fee routing and RAIL burns |
Uniswap v4 pools
Reconstruct a launch's pool key (or read factory.poolKeyOf(token)):
const [currency0, currency1] = BigInt(USDC) < BigInt(token) ? [USDC, token] : [token, USDC];
const key = { currency0, currency1, fee: 0, tickSpacing: 200, hooks: HOOK };
const poolId = keccak256(encodeAbiParameters(
[{ type: "address" }, { type: "address" }, { type: "uint24" }, { type: "int24" }, { type: "address" }],
[key.currency0, key.currency1, key.fee, key.tickSpacing, key.hooks],
));- The LP fee is 0; the hook charges the same 1% + creator tax as the curve, in USDC, rounded up.
- Buys: fee on the total quote paid. Sells: fee on the gross quote out. Exact-out amounts are grossed up so the effective rate is identical.
- Exact-in swaps paying USDC are charged up front and revert with
PartialFillNotSupportedif the pool doesn't consume the whole input (price limit hit). - Swaps initiated by the hook itself (buybacks) pay no fee.
GRADUATION_GAS_RESERVE (~450k) of gas left before attempting graduation and reverts with InsufficientGasForGraduation otherwise. Always use eth_estimateGas rather than a fixed gas limit.Errors
| Error | Meaning |
|---|---|
SlippageExceeded | Output below your minimum (curve: rate-based check; router, hook buyback). |
DeadlineExpired | Router deadline passed. |
NotTradable | Launch is not in phase 0 (curve) or 2 (pool). |
CurveGraduated | Curve completed or sold out; trade through the pool. |
PartialFillNotSupported | Pool couldn't consume the whole exact-in input; no fee charged. |
InsufficientGasForGraduation | Finishing buy sent with less than GRADUATION_GAS_RESERVE gas left. |
ZeroAmount | Amount is zero. |
NotWhitelisted | Launching is restricted and the caller isn't whitelisted. |
CreatorTaxTooHigh | creatorTaxBps > maxCreatorTaxBps. |
PairTokenNotApproved | Quote asset isn't approved. |
LaunchEconomicsMismatch | expectedEconomics doesn't match previewLaunchEconomics (config or economics changed). |
LaunchConfigDisabled / LaunchConfigUnknown | Bad launchConfigId. |
ExemptionListTooLong | More than MAX_EXEMPTIONS early-buy wallets. |
InvalidBuybackMode | buybackMode not 0 or 1. |
InvalidParams | e.g. launchTokenWithPermit with quoteIn > 0 and pairToken ≠ launchFeeToken. |
InvalidLaunchConfig | Owner config rejected: supply ≠ SUPPLY or graduation price not representable in v4. |
NotCreator | enableBuyback caller is neither deployer nor fee recipient. |
BuybackAlreadyEnabled | Buyback is already on. |
InternalSwapRequiresOperator | Burn-mode sweep with a buyback share called by a non-operator. |
WrongGraduationPhase | createGraduatedPool/rescueLaunch outside phase 1. |
RescueTooEarly | RESCUE_DELAY hasn't elapsed since the sweep. |
UnknownToken | Address isn't a rail launch. |
NoEligibleHolders | distributeRewards with eligibleSupply 0. |
NotOperator / ExceedsBudget / PlatformTokenNotSet | ProtocolFeeRouter.buyAndBurn preconditions. |
NotAuthorized / ZeroAddress / InvalidBps | Access or argument checks. |