# Fees and referrals

## Where fees come from

Every trade pays a fee on the **quote leg**, in the quote asset, from the first trade onward.
There are two components:

| Component | Goes to | Set by |
|---|---|---|
| curve / hook fee | split between the protocol and the creator | the launch config and fee policy |
| creator tax | entirely to the creator, never split | `TokenParams.creatorTaxBps` at launch |

Both are frozen into the launch at creation. A later policy change by the owner affects only
launches created after it, so an existing token's economics cannot be altered underneath its
holders.

## Referrals

A referred trade is cheaper for the trader **and** pays the referrer:

- the trader pays a discount on the standard fee,
- the referrer receives a share of what is paid,
- the pool books the remainder.

Bindings are **permanent and never rewritten**. A user can bind themselves ahead of time with
`referralRegistry.setReferrer(address)`, or a referrer can be passed to the four-argument
`curve.buy` on their first trade.

On the curve, an invalid referrer **reverts the buy**. In the pool, the hook is far more
forgiving — a rejected binding just charges the standard fee rather than failing the swap.

One security property worth knowing if you build a router: pool-phase `hookData` can only
ever name the **referrer**, never the trader. The trader always comes from `msgSender()`. So
nobody can bind a stranger's referrer with a dust swap.

## Claiming

Everything owed to anyone accumulates in the **fee escrow**, and every claim is **pull-only**
by `msg.sender` — you cannot claim on someone else's behalf. That is deliberate: it means a
recipient who cannot receive a transfer (a blocklisted address, a reverting contract) can
never block a sweep for everybody else.

```ts
const owed = await client.readContract({
  address: FEE_ESCROW, abi: escrowAbi,
  functionName: "balanceOfToken", args: [account, USDC],
});

if (owed > 0n) {
  await walletClient.writeContract({
    address: FEE_ESCROW, abi: escrowAbi, functionName: "claimToken", args: [USDC],
  });
}
```

> [!WARNING]
> The escrow stores **one balance per (recipient, token)**. It does not distinguish creator
> fees from referral fees — that split is attribution derived off-chain from events. So
> `claimToken` withdraws **both at once**, and a UI showing them as two separately claimable
> pots is lying about what the button does. Show the split as attribution; make one claim.

Fees on Arc are the 6-decimal USDC ERC-20, so `claimToken` is the path. The native
`claim()` is unreachable unless a launch quotes in the native asset, which no approved pair
token does.

## Pool-phase fees need a sweep first

Curve fees accrue in the curve and are swept to the escrow by `curve.sweepFees()`. Pool fees
accrue in the hook and are swept by `hook.sweepPoolFees(poolId, minOut)` — which may need to
convert memecoin-denominated inventory back to the quote asset against the pool's own
liquidity, which is why it takes a slippage bound.

Referral accruals in the pool are settled with `hook.claimReferralFees(referrer, currency)`,
which is **permissionless** — anyone can settle anyone's accrual into the escrow. The referrer
then claims from the escrow as above. Two steps, not one.