# Contracts

Every deployed contract: functions, events and errors.

## FociLaunchFactory

`0xa392D6eca5242715517eeCd43406aeD19424FAC0`


### Functions an application calls

### `canLaunch(address)`

`0x58373f04` · `view` · view

Whether `launcher` may launch right now: true while the public gate is open, and true for whitelisted addresses while it is closed. The same predicate `launchToken` enforces on its caller, exposed so routers like FociLaunchAndBuy can hold their own callers to this single list instead of maintaining a second one.

`launchEnabled` is left FALSE by deployment. Always check this before showing a launch form.


**Parameters**

| Name | Type | Description |
|---|---|---|
| `launcher` | `address` |  |


**Returns**

| Name | Type | Description |
|---|---|---|
| `arg0` | `bool` |  |

### `createGraduatedPool(address)`

`0x2f53ef2f` · `nonpayable` · Permissionless and retryable.

Initializes the V4 pool with the swept reserves, mints a full-range position directly to the locker, and registers the pool with the meme hook. The curve already holds the pool's quote asset, so this seeds with exactly what it swept and needs no slippage bound. Permissionless and retryable: a launch stays in Swept until a seed succeeds, so a transient failure can never strand reserves.

Seeds the V4 pool and locks the position. Like `graduate` this normally runs inside the crossing buy — but it costs roughly 900k gas on its own, and under EIP-150's 63/64 rule a buy sized by a naive `estimateGas` starves it. When that happens the curve emits `AutoSeedFailed` and the launch sits in phase 1 with no tradeable venue until someone calls this. Run a keeper on that event. Send an explicit gas limit; estimation is reliable here because there is no try/catch to hide the cost.


**Parameters**

| Name | Type | Description |
|---|---|---|
| `token` | `address` |  |


**Returns**

| Name | Type | Description |
|---|---|---|
| `positionId` | `uint256` |  |


**Reverts**

- `WrongGraduationPhase` — not in the Swept phase
- `GraduationSeedNotViable`

### `getLaunchConfig(uint256)`

`0x1cad862d` · `view` · view

Returns one token launch configuration.


**Parameters**

| Name | Type | Description |
|---|---|---|
| `id` | `uint256` |  |


**Returns**

| Name | Type | Description |
|---|---|---|
| `arg0` | `(uint256,uint256,uint256,uint256,uint24,int24,bool)` |  |

### `getLaunchedToken(address)`

`0x3cf28b5a` · `view` · view

Returns the immutable record for a token created by this factory.

Returns a ZEROED struct for an unknown token rather than reverting — check `.exists`. This is also where you get the per-launch `curve` address, since curves have no fixed deployment.


**Parameters**

| Name | Type | Description |
|---|---|---|
| `token` | `address` |  |


**Returns**

| Name | Type | Description |
|---|---|---|
| `arg0` | `(address,address,address,address,address,uint256,uint24,int24,uint16,uint8,uint256,uint256,uint256,bool)` |  |

### `graduate(address)`

`0xff6d8d05` · `nonpayable` · Permissionless — anyone may call it.

Sweeps the curve's remaining quote and token reserves into this factory and halts curve trading. Purely internal to the curve's own balances, so it is safe for the curve to call this automatically the instant a buy crosses the graduation threshold.

Normally runs automatically inside the buy that crosses the threshold. Call it manually only when that inner attempt failed, which the curve reports by emitting `AutoGraduationFailed`.


**Parameters**

| Name | Type | Description |
|---|---|---|
| `token` | `address` |  |


**Reverts**

- `WrongGraduationPhase` — already swept
- `NotReadyToGraduate` — the curve is not finished

### `launchFee()`

`0xcf3cf573` · `view` · view


**Returns**

| Name | Type | Description |
|---|---|---|
| `arg0` | `uint256` |  |

### `launchFeeToken()`

`0xbd03b5fc` · `view` · view

A flat charge on creating a launch, in `launchFeeToken`. Spam friction rather than revenue. Zero disables it outright: the payment path is skipped entirely, so nothing is pulled and no approval is needed while it is off. ERC-20 rather than native even where a chain's native asset is the same asset: the atomic launch router funds itself through `transferFrom`, and a native fee would make one call carry both an approval and attached value for what is economically one token.


**Returns**

| Name | Type | Description |
|---|---|---|
| `arg0` | `address` |  |

### `launchToken((string,string,string,string,(string,string,string,string,string),address,uint16,bytes32,bytes32),uint256,address)`

`0xbc9bc035` · `nonpayable` · Permissionless, but gated by `canLaunch(msg.sender)` — check it before offering a launch UI.

Deploys a bonding curve and its launch token, wires them together, and records the launch. Trading starts immediately on the curve; the graduation pool's pairToken is fixed here, chosen by the caller.

**Approve first:** `factory` for launchFee() of launchFeeToken() (USDC, whatever the launch quotes in).

Use this when there is NO opening buy. The atomic router reverts on a zero `quoteIn`, so it cannot be used as a plain deployer. Note the approval target differs from the router path: the factory pulls the fee from `msg.sender` itself. If `launchFee()` is zero, no approval is needed at all. A token launched this way starts with the creator holding none of its supply.


**Parameters**

| Name | Type | Description |
|---|---|---|
| `params` | `(string,string,string,string,(string,string,string,string,string),address,uint16,bytes32,bytes32)` |  |
| `launchConfigId` | `uint256` |  |
| `pairToken` | `address` |  |


**Returns**

| Name | Type | Description |
|---|---|---|
| `token` | `address` |  |
| `curve` | `address` |  |


**Reverts**

- `NotWhitelisted` — `launchEnabled` is false and you are not a whitelisted launcher
- `InvalidLaunchConfigId` — no such config
- `InvalidTokenParams` — empty name or symbol
- `CreatorTaxTooHigh` — `creatorTaxBps` above `maxCreatorTaxBps()`
- `PairTokenNotApproved` — quote asset not approved by the owner
- `LaunchEconomicsMismatch(expected,actual)` — terms moved since you read `previewLaunchEconomics`
- `FailedDeployment` — you already used this `salt`; salts are namespaced per account
- ERC-20 revert on the fee transfer — insufficient allowance **to the factory**

### `pairTokenEconomics(address)`

`0x31082134` · `view` · view

Phantom reserve and graduation threshold in the QUOTE ASSET'S OWN DECIMALS (6 for USDC/EURC, 8 for cirBTC, 18 for WETH/XAUM).


**Parameters**

| Name | Type | Description |
|---|---|---|
| `pairToken` | `address` |  |


**Returns**

| Name | Type | Description |
|---|---|---|
| `phantomQuote` | `uint256` |  |
| `graduationThreshold` | `uint256` |  |
| `decimals` | `uint8` |  |

### `previewLaunchEconomics(uint256,address)`

`0xf718b78c` · `view` · view

Returns the economics digest a launch of `launchConfigId` in `pairToken` would produce right now, for a creator to pass back as TokenParams.expectedEconomics.

Reading the digest and launching in separate transactions still leaves the terms free to move in between; the pin is what makes that movement revert instead of silently repricing the launch.

Returns the digest to put in `TokenParams.expectedEconomics`. Fetch it in the same flow as the submit — it covers the launch fee, so an owner changing `setLaunchFee` between your quote and your signature invalidates it. Passing `bytes32(0)` waives the check entirely, which means accepting whatever terms are live when the transaction lands.


**Parameters**

| Name | Type | Description |
|---|---|---|
| `launchConfigId` | `uint256` |  |
| `pairToken` | `address` |  |


**Returns**

| Name | Type | Description |
|---|---|---|
| `arg0` | `bytes32` |  |


### Other functions

| Signature | Selector | Mutability |
|---|---|---|
| `acceptOwnership()` | `0x79ba5097` | nonpayable |
| `addLaunchConfig((uint256,uint256,uint256,uint256,uint24,int24,bool))` | `0x0e5b0aae` | nonpayable |
| `approvedPairTokens(address)` | `0x9831705e` | view |
| `cancelCreatorFeeRecipientChange(address)` | `0x6e47a188` | nonpayable |
| `CREATOR_FEE_RECIPIENT_EXECUTION_WINDOW()` | `0x02d4753d` | view |
| `CREATOR_FEE_RECIPIENT_TIMELOCK()` | `0x5a83b00a` | view |
| `executeCreatorFeeRecipientChange(address)` | `0x3d3d2d58` | nonpayable |
| `feeEscrow()` | `0xc4b7de97` | view |
| `forceSweptGraduation(address)` | `0x7aed273e` | nonpayable |
| `getLaunchFeePolicy(address)` | `0x470ef5fc` | view |
| `GRADUATION_RESCUE_DELAY()` | `0x2d1250b8` | view |
| `graduationExecutor()` | `0xcc6d7a39` | view |
| `graduationGuard()` | `0x496aa100` | view |
| `launchConfigCount()` | `0xae72d871` | view |
| `launchDeployer()` | `0x858f5964` | view |
| `launchEnabled()` | `0x236a4afb` | view |
| `launchForwarder()` | `0x9b924452` | view |
| `launchTokenFor((string,string,string,string,(string,string,string,string,string),address,uint16,bytes32,bytes32),uint256,address,address)` | `0x266101cb` | nonpayable |
| `locker()` | `0xd7b96d4e` | view |
| `maxCreatorTaxBps()` | `0xf325a5fb` | view |
| `memeHook()` | `0x6651812c` | view |
| `owner()` | `0x8da5cb5b` | view |
| `pendingCreatorFeeRecipient(address)` | `0x9beacf4a` | view |
| `pendingOwner()` | `0xe30c3978` | view |
| `permit2()` | `0x12261ee7` | view |
| `poolManager()` | `0xdc4c90d3` | view |
| `positionManager()` | `0x791b98bc` | view |
| `renounceOwnership()` | `0x715018a6` | pure |
| `rescueCurveFees(address)` | `0x189eb0f5` | nonpayable |
| `rescueSweptGraduation(address,address)` | `0xdbcb9c76` | nonpayable |
| `setCreatorFeeRecipient(address,address)` | `0xe102c9aa` | nonpayable |
| `setGraduationExecutor(address)` | `0xfbec2d8b` | nonpayable |
| `setLaunchDeployer(address)` | `0x3a9391e8` | nonpayable |
| `setLaunchEnabled(bool)` | `0xf56f05b2` | nonpayable |
| `setLaunchFee(address,uint256)` | `0x6e51833f` | nonpayable |
| `setLaunchForwarder(address)` | `0x767b7c16` | nonpayable |
| `setMaxCreatorTaxBps(uint256)` | `0x2260aead` | nonpayable |
| `setPairTokenApproved(address,bool)` | `0x8763e3dc` | nonpayable |
| `setPairTokenEconomics(address,uint256,uint256,uint8)` | `0x092c08bd` | nonpayable |
| `setWhitelistedLauncher(address,bool)` | `0x366f0f3e` | nonpayable |
| `transferCreatorFeeRecipient(address,address)` | `0x2931861b` | nonpayable |
| `transferOwnership(address)` | `0xf2fde38b` | nonpayable |
| `updateLaunchConfig(uint256,(uint256,uint256,uint256,uint256,uint24,int24,bool))` | `0xe73e334a` | nonpayable |
| `whitelistedLaunchers(address)` | `0xda3eda65` | view |


### Events

| Event | topic0 |
|---|---|
| `CreatorFeeRecipientChangeCancelled(address,address)` | `0xbe2de91c1cbef653c760573fff8355c0c851d35ed2a898342b4db556301cccf4` |
| `CreatorFeeRecipientChangeProposed(address,address,address,uint256,uint256)` | `0x7f119e44c84a715429bee60d30ad2e14afdef6c60bb1a7eaa01290ecf6d1b2e5` |
| `CreatorFeeRecipientUpdated(address,address,address)` | `0x308c390ed1ab5873392818e036cabdf408bc8ad042fbaead3108954ff75ba980` |
| `GraduationExecutorSet(address)` | `0xac04674474e93058fae25e6df5dd94f57cdcacfe560a182a2eefc8c6006fbf6f` |
| `GraduationTokensPermanentlyLocked(address,uint256)` | `0xa0a18f5bf205becee8b268d7cf69addab8548ae8ef361791464cf0e0e17c1361` |
| `LaunchConfigAdded(uint256)` | `0xedd96c570c6e5ef9add0378e59df53579a283889dc5dab6440ef6eca2ee6c8ce` |
| `LaunchConfigUpdated(uint256)` | `0x2f8ba78ae68cfd0c82c7756c540eaf4eead3341aef9ccebcb91d546bff10d62b` |
| `LaunchDeployerSet(address)` | `0xd5ea7aa3e328a0594dcf6914cd9e5369779efaa194ee4dd4c5afcad4f4ebbb0c` |
| `LaunchEnabledUpdated(bool)` | `0x4f1ea5016c51c2f82324e00e9b8a4a95ee5aeaa10c653dabaec5f1bc9047ba0b` |
| `LaunchFeeUpdated(address,uint256)` | `0xd0766d3f1431146228fe8edef25f27842a1669c91d46e4af1b73405b354489a5` |
| `LaunchForceSwept(address)` | `0x52c1a28345695afc7f6b7629133124dec5d61ee745affd65e4fd2a776bc05840` |
| `LaunchForwarderSet(address)` | `0x56b32d3633fed72f97c4df44a78b5fa04f1d662d4bddebcd8a9b216d26d093ad` |
| `LaunchGraduationRescued(address,address,uint256,uint256)` | `0x7017304fdd491394686dce984eac721f0be1a22228346210f16694772bde44ca` |
| `LaunchSwept(address,uint256,uint256)` | `0xcdb72f157fd3666758a6ce201387ffb52038c7562e4fff352828da1096c4b6b4` |
| `MaxCreatorTaxUpdated(uint256)` | `0x3e99ceb3e222d2214d53dacca902810db845f156f78152fdc076be628c4e9a40` |
| `OwnershipTransferred(address,address)` | `0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0` |
| `OwnershipTransferStarted(address,address)` | `0x38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e22700` |
| `PairTokenApprovalUpdated(address,bool)` | `0x060d1992d069dc524985f328329aae36102a017c59733c5c91fc0691ee0703b6` |
| `PairTokenEconomicsUpdated(address,uint256,uint256,uint8)` | `0x67d517ee0e305d608b8410ddef27bbd2ed964d843d9b936e84ea2ad1bd65e5d1` |
| `PoolGraduated(address,uint256,uint256,uint256)` | `0x0a44ef75df69c534f43cd6c1aa3ef8983065fe5fe79ef9e79f6494e6f258c259` |
| `TokenLaunched(address,address,address,address,uint256,uint256)` | `0x8d4aad4953d0ca700d468f3753aa14432d1b35b43ec6409f051fb6aa43a89607` |
| `WhitelistedLauncherUpdated(address,bool)` | `0xef2b562a67f01ed4b7c4265ec09b539039c6d5dd7e752191d3940508c3dc0068` |


### Errors

| Error | Selector |
|---|---|
| `AlreadySet()` | `0xa741a045` |
| `CombinedFeeTooHigh()` | `0x49e55bcb` |
| `CoreLpFeeMustBeZero()` | `0x85258712` |
| `CreatorTaxTooHigh()` | `0x9ad465dc` |
| `CurveFeeTooHigh()` | `0x4e222a24` |
| `CurveNotQuotable()` | `0x95e32dab` |
| `FeeTransferFailed()` | `0x4033e4e3` |
| `GraduationExecutorNotSet()` | `0xd43cabc3` |
| `GraduationRescueTooEarly(uint256)` | `0xbdcd75af` |
| `GraduationSeedNotViable()` | `0x2c37d0eb` |
| `GraduationStillViable()` | `0x6d3bcfe5` |
| `InexactTransfer(address,uint256,uint256)` | `0x495a9962` |
| `InvalidBasisPoints()` | `0x800c7e91` |
| `InvalidGraduationThreshold()` | `0x2bb8bdd6` |
| `InvalidLaunchConfigId()` | `0x68b42c59` |
| `InvalidPhantomQuote()` | `0x2b7ad4f8` |
| `InvalidTickSpacing()` | `0x270815a0` |
| `InvalidTokenParams()` | `0x374852ca` |
| `LaunchConfigDisabled()` | `0xa8b63076` |
| `LaunchDependenciesNotWired()` | `0x1de25df3` |
| `LaunchDeployerNotSet()` | `0x57332dcf` |
| `LaunchEconomicsMismatch(bytes32,bytes32)` | `0xecb27319` |
| `LaunchFeeTokenNotSet()` | `0x52660db0` |
| `NoPendingChange()` | `0xa3fef2f8` |
| `NotCreatorFeeRecipient()` | `0xb9f93944` |
| `NothingToGraduate()` | `0xc2074c46` |
| `NotLaunchForwarder()` | `0xea9eaa96` |
| `NotReadyToGraduate()` | `0xffa32558` |
| `NotWhitelisted()` | `0x584a7938` |
| `OwnableInvalidOwner(address)` | `0x1e4fbdf7` |
| `OwnableUnauthorizedAccount(address)` | `0x118cdaa7` |
| `OwnershipCannotBeRenounced()` | `0x2fab92ca` |
| `PairTokenDecimalsMismatch(uint8,uint8)` | `0x4e3de34f` |
| `PairTokenDecimalsUnavailable()` | `0xe43c14ca` |
| `PairTokenEconomicsInvalid()` | `0x764c63c8` |
| `PairTokenNotApproved()` | `0x49285dfb` |
| `PairTokenValidationFailed()` | `0x26fbfa60` |
| `ReentrancyGuardReentrantCall()` | `0x3ee5aeb5` |
| `SafeERC20FailedOperation(address)` | `0x5274afe7` |
| `SqrtPriceOutOfBounds()` | `0x582157bb` |
| `SupplyTooHigh()` | `0xacb9fa2b` |
| `SupplyTooLow()` | `0xc0b4e373` |
| `TimelockExpired(uint256)` | `0xb79d40e8` |
| `TimelockNotElapsed(uint256)` | `0x810c4f2a` |
| `TokenNotFound()` | `0xcbdb7b30` |
| `UnsupportedPrice()` | `0xdd737e7c` |
| `WrongGraduationPhase()` | `0x9465dbd4` |
| `ZeroAddress()` | `0xd92e233d` |
| `ZeroAmount()` | `0x1f2a2005` |

---
## FociBondingCurve

Deployed **once per launch** — no fixed address. Read it from `factory.getLaunchedToken(token)`.


### Functions an application calls

### `buy(uint256,uint256,address)`

`0x59a87bc1` · `payable` · Permissionless. Reverts once the launch has graduated.

Buys the launch token with this launch's quote asset. The fee is always taken from the quote leg, so this curve never holds a memecoin-denominated fee.

`quoteIn` must equal `msg.value` for a native launch, and must be accompanied by no value at all for an ERC-20 launch. The credited amount for an ERC-20 is the observed balance delta rather than the requested amount, so a fee-on-transfer quote asset cannot make the curve promise reserves it never received. A buy that would take the curve past its reserved allocation is filled only up to that allocation, charged for what it actually received, and refunded the difference. It is deliberately not rejected: the last buy of a launch is the one most likely to be sized against a state someone else has already moved, and reverting would let anyone grief it by slipping a small buy in ahead. Partial fills reinterpret `minTokensOut` as a bound on price rather than on quantity, since a caller who spends less than they offered cannot expect the whole quantity they asked for. The requirement is that the price paid is no worse than the price implied by the caller's own arguments, and when nothing is clamped it reduces exactly to `tokensOut >= minTokensOut`.

**Approve first:** `curve` for quoteIn of the launch's quote asset (getLaunchedToken(token).pairToken), in its own decimals.

`minTokensOut` is a PRICE bound, not a quantity bound. A buy that would exceed the remaining allocation is clamped rather than reverted, and the remainder is refunded to `msg.sender` in the same transaction — so you can safely overshoot the graduation threshold.


**Parameters**

| Name | Type | Description |
|---|---|---|
| `quoteIn` | `uint256` |  |
| `minTokensOut` | `uint256` |  |
| `recipient` | `address` |  |


**Returns**

| Name | Type | Description |
|---|---|---|
| `tokensOut` | `uint256` |  |


**Reverts**

- `CurveGraduated` — the curve is closed; trade the V4 pool instead
- `SlippageExceeded(tokensOut,minTokensOut)` — the effective price broke your bound
- `UnexpectedNativeValue` — sent ETH on an ERC-20-quoted launch; `msg.value` must be 0
- `ZeroAmount` — the transfer delivered nothing

### `buy(uint256,uint256,address,address)`

`0x82b2a559` · `payable` · Permissionless. Reverts once the launch has graduated.

Buys with a referrer attached.

The referrer is only consulted the first time `recipient` trades with one; after that the binding in the registry stands and this argument is ignored, so passing a fresh referrer cannot poach an existing relationship. An unusable referrer (the recipient themselves, or someone the recipient already refers) reverts rather than being dropped, because it is an argument the caller chose to supply and silently charging them the undiscounted fee would be worse.

**Approve first:** `curve` for quoteIn of the launch's quote asset (getLaunchedToken(token).pairToken), in its own decimals.

As above, with a referrer bound to `recipient` (not to `msg.sender`). The binding is written only on that address's first referred trade and is permanent thereafter. An unusable referrer REVERTS THE WHOLE BUY — `SelfReferral` and `ReciprocalReferral` are not swallowed — so resolve and validate a referrer before putting it in the call.


**Parameters**

| Name | Type | Description |
|---|---|---|
| `quoteIn` | `uint256` |  |
| `minTokensOut` | `uint256` |  |
| `recipient` | `address` |  |
| `referrer` | `address` | The account to credit for this recipient's trades, or the zero address to trade under whatever binding already exists. |


**Returns**

| Name | Type | Description |
|---|---|---|
| `tokensOut` | `uint256` |  |


**Reverts**

- `SelfReferral` — referrer is the recipient
- `ReciprocalReferral` — the recipient already refers that address
- everything the 3-argument overload throws

### `getReserves()`

`0x0902f1ac` · `view` · view

Returns the curve's current tradeable reserves, excluding fees pending sweep.

Returns the VIRTUAL reserves — `quoteReserve` includes the phantom reserve. For the amount actually raised use `realQuoteReserve()`.


**Returns**

| Name | Type | Description |
|---|---|---|
| `quoteReserve_` | `uint256` |  |
| `tokenReserve_` | `uint256` |  |

### `graduated()`

`0xe7c2b772` · `view` · view


**Returns**

| Name | Type | Description |
|---|---|---|
| `arg0` | `bool` |  |

### `quoteReserve()`

`0x9da771f4` · `view` · view

Tradeable quote reserve only, matching IFociBondingCurve.


**Returns**

| Name | Type | Description |
|---|---|---|
| `quoteReserve_` | `uint256` |  |

### `readyToGraduate()`

`0xc68360a5` · `view` · view

True once the curve's sellable allocation has been bought out.

Equivalent to the real quote reserve reaching `graduationThreshold`, since the reserved balance is derived from that same point. Expressed against the token side because that is the one a buy cannot overshoot: the quote side is a floor that a large trade could sail past, while the token side is a hard stop the curve refuses to cross.

True when the sellable allocation reaches zero, which is by construction the same point as the real quote reserve reaching the graduation threshold.


**Returns**

| Name | Type | Description |
|---|---|---|
| `arg0` | `bool` |  |

### `realQuoteReserve()`

`0x4f1f58fd` · `view` · view

Returns physically held tradeable quote asset, excluding virtual liquidity and balances already earmarked as fees or creator tax.


**Returns**

| Name | Type | Description |
|---|---|---|
| `arg0` | `uint256` |  |

### `sell(uint256,uint256,address)`

`0xd04c6983` · `nonpayable` · Permissionless. Closes the instant `readyToGraduate()` is true.

Sells the launch token back to the curve for the quote asset. The fee is taken from the quote output, so it is always quote-denominated here too.

Closed once the sellable allocation is exhausted, not merely once `graduated` is set. `_tryAutoGraduate` swallows a failed graduation so a problem there cannot take the crossing buy down with it, which leaves a window where the curve is ready but the flag is still false. `buy` already refuses that state through its own `sellable == 0` check, and `sell` has to match: `graduate` hands the pool whatever `trackedTokens` holds, so a sell landing in the window would put tokens back on the curve and take quote off it, and the pool would then be seeded deeper and cheaper than the reserved allocation fixes it at. The deterministic graduation price only holds if the window is closed on both sides. This cannot strand a holder. `graduate` is permissionless, so anyone blocked here can settle the launch themselves in the same transaction and trade the V4 pool instead.

**Approve first:** `curve` for tokensIn of the MEMECOIN (18 decimals).

Unlike buy, `minQuoteOut` is a strict QUANTITY bound. The sell side shuts the moment the allocation is exhausted — before the `graduated` flag is even set — so a sell can start reverting with `CurveGraduated` while the UI still shows a live curve. Approve the CURVE for the memecoin, not the factory.


**Parameters**

| Name | Type | Description |
|---|---|---|
| `tokensIn` | `uint256` |  |
| `minQuoteOut` | `uint256` |  |
| `recipient` | `address` |  |


**Returns**

| Name | Type | Description |
|---|---|---|
| `quoteOut` | `uint256` |  |


**Reverts**

- `CurveGraduated` — the allocation is exhausted or the curve has graduated
- `SlippageExceeded(quoteOut,minQuoteOut)`
- `ZeroAmount`

### `sellableTokens()`

`0x808bcddc` · `view` · view

Tokens still available to buy before the curve graduates.


**Returns**

| Name | Type | Description |
|---|---|---|
| `arg0` | `uint256` |  |


### Other functions

| Signature | Selector | Mutability |
|---|---|---|
| `creatorTaxBalance()` | `0xdb2bd533` | view |
| `creatorTaxBps()` | `0xc1bb8901` | view |
| `deployer()` | `0xd5f39488` | view |
| `factory()` | `0xc45a0155` | view |
| `feeBps()` | `0x24a9d853` | view |
| `feeEscrow()` | `0xc4b7de97` | view |
| `feePolicy()` | `0x82589038` | view |
| `graduate(address)` | `0xff6d8d05` | nonpayable |
| `graduationThreshold()` | `0x8b0bc501` | view |
| `initialize(address)` | `0xc4d66de8` | nonpayable |
| `isNativeQuote()` | `0xdc08e094` | view |
| `launchedAt()` | `0xbf56b371` | view |
| `launchSupply()` | `0x3f7ed6b7` | view |
| `maxInternalPriceImpactBps()` | `0x90addc1e` | view |
| `pairToken()` | `0x3de35b79` | view |
| `phantomQuote()` | `0xc57eadfc` | view |
| `protocolFeeRecipient()` | `0x64df049e` | view |
| `protocolFeeShareBps()` | `0x9040f866` | view |
| `quoteFeeBalance()` | `0xed479c47` | view |
| `referralDiscountBps()` | `0x30ab6943` | view |
| `referralRegistry()` | `0x4e627e62` | view |
| `referralShareBps()` | `0x47c9bc2d` | view |
| `rescueFees()` | `0x52920587` | nonpayable |
| `reservedTokens()` | `0x15a55347` | view |
| `setCreatorFeeRecipient(address)` | `0x7b04ea62` | nonpayable |
| `sweepFees()` | `0xd113b95c` | nonpayable |
| `token()` | `0xfc0c546a` | view |
| `tokenReserve()` | `0xcbcb3171` | view |
| `trackedQuote()` | `0xca52b0b7` | view |
| `trackedTokens()` | `0x4c37ef23` | view |


### Events

| Event | topic0 |
|---|---|
| `AutoGraduationFailed(address,uint256)` | `0xe2cd2f31ebc05ec28640102987f4c8fc5f20e269e1b3aa82577f3f2f0e35c7c6` |
| `AutoSeedFailed(address,uint256)` | `0x2cbe77dadc7f8418071409bebfd71778263eecb998af52aa5c9e27b995a71676` |
| `CreatorFeeRecipientUpdated(address,address)` | `0x2cc664e1ac1e2d05c0d4637bb63ec8189113b6ac39276be8977e26216a8cdd19` |
| `CurveBuy(address,address,uint256,uint256,uint256,uint256)` | `0xec36bf571f136799e8dc0b0b8bea4b04d8bd3d43de838aab0d5fc21d4cbfc455` |
| `CurveBuyRefunded(address,uint256)` | `0xa69e8258ccc7b9bbb70ab953fc2d1062b4ee28b8ca827534097e1732e87b0262` |
| `CurveCompleted(address,uint256,uint256)` | `0xf8d37a90738ae063b8b8058b66f5880cf3cf7ab0c5d4fa78219696591dfbfb67` |
| `CurveSell(address,address,uint256,uint256,uint256,uint256)` | `0x8113d738abdcb6b38357e9d53a54a7157861a09031b453651f0fe7fe151f59df` |
| `FeesRescued(address,address,uint256,uint256)` | `0x6460dc5c867a0678a8bcc5e64f629fae539901c53a4a8b42fe21d7a6c5e6437d` |
| `FeesSwept(uint256,uint256)` | `0xaf739f46ca7a23c9f259838ec2c5249acf4e1cf9fe68a46f77c3dfa452eda605` |
| `Initialized(address)` | `0x908408e307fc569b417f6cbec5d5a06f44a0a505ac0479b47d421a4b2fd6a1e6` |
| `ReferralFeePaid(address,address,uint256)` | `0xde9bddf476dde28b26de9d0b38bb9811ebb9d4945cd0c7feadd215c28fe09717` |


### Errors

| Error | Selector |
|---|---|
| `AlreadyGraduated()` | `0xe6a0d45f` |
| `AlreadyInitialized()` | `0x0dc149f0` |
| `CurveGraduated()` | `0x025ac17e` |
| `InsufficientInputAmount()` | `0x098fb561` |
| `InsufficientLiquidity()` | `0xbb55fd27` |
| `InsufficientOutputAmount()` | `0x42301c23` |
| `InvalidFeePolicy()` | `0x7a34030f` |
| `InvalidLaunchEconomics()` | `0xbc0ecfe3` |
| `NativeValueMismatch(uint256,uint256)` | `0xbc760cfe` |
| `NotFactory()` | `0x32cc7236` |
| `NotFeeSweepOperator()` | `0x8d42130c` |
| `NotInitialized()` | `0x87138d5c` |
| `NotReadyToGraduate()` | `0xffa32558` |
| `ReentrancyGuardReentrantCall()` | `0x3ee5aeb5` |
| `SafeERC20FailedOperation(address)` | `0x5274afe7` |
| `SlippageExceeded(uint256,uint256)` | `0x71c4efed` |
| `TransferFailed()` | `0x90b8ec18` |
| `UnexpectedNativeValue()` | `0xe0aeda7d` |
| `ZeroAddress()` | `0xd92e233d` |
| `ZeroAmount()` | `0x1f2a2005` |

---
## FociLauncherToken

Deployed **once per launch** — no fixed address. Read it from `factory.getLaunchedToken(token)`.


### Other functions

| Signature | Selector | Mutability |
|---|---|---|
| `allowance(address,address)` | `0xdd62ed3e` | view |
| `approve(address,uint256)` | `0x095ea7b3` | nonpayable |
| `balanceOf(address)` | `0x70a08231` | view |
| `burn(uint256)` | `0x42966c68` | nonpayable |
| `burnFrom(address,uint256)` | `0x79cc6790` | nonpayable |
| `curve()` | `0x7165485d` | view |
| `decimals()` | `0x313ce567` | view |
| `deployer()` | `0xd5f39488` | view |
| `description()` | `0x7284e416` | view |
| `getTokenInfo()` | `0xabb1dc44` | view |
| `launchFactory()` | `0x536dac9b` | view |
| `logo()` | `0xfb7f21eb` | view |
| `name()` | `0x06fdde03` | view |
| `socials()` | `0x53cd512a` | view |
| `symbol()` | `0x95d89b41` | view |
| `totalSupply()` | `0x18160ddd` | view |
| `transfer(address,uint256)` | `0xa9059cbb` | nonpayable |
| `transferFrom(address,address,uint256)` | `0x23b872dd` | nonpayable |


### Events

| Event | topic0 |
|---|---|
| `Approval(address,address,uint256)` | `0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925` |
| `Transfer(address,address,uint256)` | `0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef` |


### Errors

| Error | Selector |
|---|---|
| `ERC20InsufficientAllowance(address,uint256,uint256)` | `0xfb8f41b2` |
| `ERC20InsufficientBalance(address,uint256,uint256)` | `0xe450d38c` |
| `ERC20InvalidApprover(address)` | `0xe602df05` |
| `ERC20InvalidReceiver(address)` | `0xec442f05` |
| `ERC20InvalidSender(address)` | `0x96c6fd1e` |
| `ERC20InvalidSpender(address)` | `0x94280d62` |
| `ZeroAddress()` | `0xd92e233d` |

---
## FociLaunchAndBuy

`0x5C5c202271E1300bD5Ce43A4F5C1cEA8efd57B63`


### Functions an application calls

### `launchAndBuy((string,string,string,string,(string,string,string,string,string),address,uint16,bytes32,bytes32),uint256,address,uint256,uint256,address)`

`0x32b6091a` · `payable` · Permissionless, but `factory.canLaunch(msg.sender)` must hold — routing through the router does not widen the gate.

Launches a token and immediately buys `quoteIn` of its curve for `recipient`, both in this transaction.

A native launch carries the opening buy as `msg.value`. An ERC-20 launch carries no value at all and the buy is pulled from the caller, who must have approved this contract for `quoteIn` first.

**Approve first:** `launchAndBuy` for launchFee() in the fee token + quoteIn in the quote — one approval for the sum only when both are USDC, otherwise one per asset of launchFeeToken() (USDC) for the fee AND the launch's pairToken for the opening buy.

Deploys the token and performs the creator's opening buy in one transaction. Two things differ from the direct path: `creatorFeeRecipient` may NOT be zero here (the direct path defaults it to the caller), and `quoteIn` may not be zero — the router exists to buy. GAS: if the opening buy crosses the graduation threshold the curve tries to seed the V4 pool inside this same transaction, and `eth_estimateGas` cannot size that — the seed is a best-effort try/catch, so a simulation in which it fails still succeeds overall and returns a limit too small for it to work. Send an explicit generous gas limit.


**Parameters**

| Name | Type | Description |
|---|---|---|
| `params` | `(string,string,string,string,(string,string,string,string,string),address,uint16,bytes32,bytes32)` | Launch parameters, forwarded to the factory untouched. Set `creatorFeeRecipient` to the wallet that should earn the launch's fees, and `expectedEconomics` to the value `previewLaunchEconomics` returned, which still pins the terms as it would on a direct launch. |
| `launchConfigId` | `uint256` | Factory launch config to launch against. |
| `pairToken` | `address` | Quote asset, or the zero address for a native launch. |
| `quoteIn` | `uint256` | Amount of the quote asset to spend on the opening buy. An amount past what the curve can sell is clamped by the curve and the remainder comes back to the caller. |
| `minTokensOut` | `uint256` | Slippage bound on the opening buy. The curve prices a clamped fill against this too, so a buy sized to take the whole allocation can still set a meaningful floor. |
| `recipient` | `address` | Receives the purchased tokens. |


**Returns**

| Name | Type | Description |
|---|---|---|
| `token` | `address` |  |
| `curve` | `address` |  |
| `tokensOut` | `uint256` |  |


**Reverts**

- `NotApprovedLauncher` — the factory's launch gate is closed for you
- `ZeroAddress` — `recipient` or `params.creatorFeeRecipient` is zero
- `ZeroAmount` — `quoteIn` is zero; use `factory.launchToken` instead
- everything `launchToken` throws, plus an allowance failure against **the router**


### Other functions

| Signature | Selector | Mutability |
|---|---|---|
| `acceptOwnership()` | `0x79ba5097` | nonpayable |
| `factory()` | `0xc45a0155` | view |
| `owner()` | `0x8da5cb5b` | view |
| `pendingOwner()` | `0xe30c3978` | view |
| `renounceOwnership()` | `0x715018a6` | nonpayable |
| `rescue(address,address)` | `0x4fdf5d1d` | nonpayable |
| `transferOwnership(address)` | `0xf2fde38b` | nonpayable |


### Events

| Event | topic0 |
|---|---|
| `Launched(address,address,address,address,uint256,uint256)` | `0xdcacba5e347ae7abd91cb519eb877af8fa7774e347b85dd3ddcd24a2ba8cdf37` |
| `OwnershipTransferred(address,address)` | `0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0` |
| `OwnershipTransferStarted(address,address)` | `0x38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e22700` |
| `Rescued(address,address,uint256)` | `0x3af790fafda720819b2fc6e15090606e81154e0ac9a92d38ecad006d99d20ecc` |


### Errors

| Error | Selector |
|---|---|
| `NativeValueMismatch(uint256,uint256)` | `0xbc760cfe` |
| `NotApprovedLauncher()` | `0x502ba015` |
| `OwnableInvalidOwner(address)` | `0x1e4fbdf7` |
| `OwnableUnauthorizedAccount(address)` | `0x118cdaa7` |
| `ReentrancyGuardReentrantCall()` | `0x3ee5aeb5` |
| `RefundFailed()` | `0xf0c49d44` |
| `SafeERC20FailedOperation(address)` | `0x5274afe7` |
| `ZeroAddress()` | `0xd92e233d` |
| `ZeroAmount()` | `0x1f2a2005` |

---
## FociMemeHook

`0xF847790B6fA5DA300BB3f56f10d743e71E98e044`


### Functions an application calls

### `claimReferralFees(address,address)`

`0xcf893acc` · `nonpayable` · Permissionless — anyone may settle anyone's accrual into the escrow.

Pays a referrer their accrued fees for one currency into the escrow. Permissionless: the amount and destination are fixed by the ledger, so who triggers the settlement does not matter.

Deliberately not settled inside `afterSwap`. An escrow credit is an external call plus an approval, and putting it on the swap path would charge every trader for it. This mirrors how the pool's own fees are batched into `sweepPoolFees` rather than distributed per swap.

Two steps, not one: this moves the accrual into the escrow, then the referrer calls `feeEscrow.claimToken` to withdraw. Returns 0 and does nothing when the ledger is empty.


**Parameters**

| Name | Type | Description |
|---|---|---|
| `referrer` | `address` |  |
| `currency` | `address` |  |


**Returns**

| Name | Type | Description |
|---|---|---|
| `amount` | `uint256` |  |

### `currentFeePolicy()`

`0x89a69bd8` · `view` · view

Returns the policy terms new launches snapshot immutably.

Live policy. A launch freezes a copy of this at creation, so an existing launch is unaffected by later changes.


**Returns**

| Name | Type | Description |
|---|---|---|
| `arg0` | `(address,uint16,uint16,uint16,uint16,uint16)` |  |

### `pendingReferral(address,address)`

`0xd85b2777` · `view` · view


**Parameters**

| Name | Type | Description |
|---|---|---|
| `referrer` | `address` |  |
| `currency` | `address` |  |


**Returns**

| Name | Type | Description |
|---|---|---|
| `amount` | `uint256` |  |

### `sweepPoolFees(bytes32,uint256)`

`0xebe51768` · `nonpayable` · The fee-sweep operator, or the pool's creator.

Converts any pending memecoin-denominated fee into the pool's quote currency against the pool's own liquidity, then splits the combined quote-currency total between protocol and creator using the live policy, exactly mirroring the bonding curve's own sweep. The trusted sweep operator is required whenever the sweep would execute an internal conversion. The creator may still distribute already-quoted fees when no internal swap is needed.

If any memecoin-denominated fee is pending, only the operator may call and `minConversionQuoteOut` must be non-zero — the sweep converts inventory against the pool's own liquidity and needs a slippage bound.


**Parameters**

| Name | Type | Description |
|---|---|---|
| `poolId` | `bytes32` |  |
| `minConversionQuoteOut` | `uint256` |  |


### Other functions

| Signature | Selector | Mutability |
|---|---|---|
| `acceptOwnership()` | `0x79ba5097` | nonpayable |
| `afterAddLiquidity(address,(address,address,uint24,int24,address),(int24,int24,int256,bytes32),int256,int256,bytes)` | `0x9f063efc` | nonpayable |
| `afterDonate(address,(address,address,uint24,int24,address),uint256,uint256,bytes)` | `0xe1b4af69` | nonpayable |
| `afterInitialize(address,(address,address,uint24,int24,address),uint160,int24)` | `0x6fe7e6eb` | nonpayable |
| `afterRemoveLiquidity(address,(address,address,uint24,int24,address),(int24,int24,int256,bytes32),int256,int256,bytes)` | `0x6c2bbe7e` | nonpayable |
| `afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)` | `0xb47b2fb1` | nonpayable |
| `beforeAddLiquidity(address,(address,address,uint24,int24,address),(int24,int24,int256,bytes32),bytes)` | `0x259982e5` | nonpayable |
| `beforeDonate(address,(address,address,uint24,int24,address),uint256,uint256,bytes)` | `0xb6a8b0fa` | nonpayable |
| `beforeInitialize(address,(address,address,uint24,int24,address),uint160)` | `0xdc98354e` | nonpayable |
| `beforeRemoveLiquidity(address,(address,address,uint24,int24,address),(int24,int24,int256,bytes32),bytes)` | `0x21d0ee70` | nonpayable |
| `beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)` | `0x575e24b4` | nonpayable |
| `factory()` | `0xc45a0155` | view |
| `feeEscrow()` | `0xc4b7de97` | view |
| `feeSweepOperator()` | `0x8a36a6bb` | view |
| `getHookPermissions()` | `0xc4e833ce` | pure |
| `hookFeeBps()` | `0xea26abcf` | view |
| `launches(bytes32)` | `0xad091230` | view |
| `maxInternalPriceImpactBps()` | `0x90addc1e` | view |
| `owner()` | `0x8da5cb5b` | view |
| `pendingCreatorTax(bytes32,address)` | `0xc8eaa792` | view |
| `pendingFees(bytes32,address)` | `0x359b4f30` | view |
| `pendingOwner()` | `0xe30c3978` | view |
| `poolManager()` | `0xdc4c90d3` | view |
| `protocolFeeRecipient()` | `0x64df049e` | view |
| `protocolFeeShareBps()` | `0x9040f866` | view |
| `referralDiscountBps()` | `0x30ab6943` | view |
| `referralRegistry()` | `0x4e627e62` | view |
| `referralShareBps()` | `0x47c9bc2d` | view |
| `registerPool((address,address,uint24,int24,address),address,address,uint16,(address,uint16,uint16,uint16,uint16,uint16))` | `0x302511dd` | nonpayable |
| `renounceOwnership()` | `0x715018a6` | pure |
| `rescuePoolFees(bytes32)` | `0x5cbe8117` | nonpayable |
| `setCreatorFeeRecipient(bytes32,address)` | `0xed8ef7a3` | nonpayable |
| `setFactory(address)` | `0x5bb47808` | nonpayable |
| `setFeeSweepOperator(address)` | `0x54faf9c3` | nonpayable |
| `setHookFeeBps(uint256)` | `0xbfe7af83` | nonpayable |
| `setMaxInternalPriceImpactBps(uint256)` | `0xb89eddab` | nonpayable |
| `setProtocolFeeRecipient(address)` | `0xe521cb92` | nonpayable |
| `setProtocolFeeShareBps(uint256)` | `0xfc75e481` | nonpayable |
| `setReferralDiscountBps(uint256)` | `0x98da62d5` | nonpayable |
| `setReferralRegistry(address)` | `0x6a79115f` | nonpayable |
| `setReferralShareBps(uint256)` | `0xd07e995b` | nonpayable |
| `transferOwnership(address)` | `0xf2fde38b` | nonpayable |
| `unlockCallback(bytes)` | `0x91dd7346` | nonpayable |


### Events

| Event | topic0 |
|---|---|
| `CreatorFeeRecipientUpdated(bytes32,address,address)` | `0xb45e6b72a7de9a2077babe9717744436f3880e114099956ca85f91a77469a532` |
| `FactorySet(address)` | `0x1edf3afd4ac789736e00d216cd88be164ddcef26a6eedcc30cdb0cb62f3741b1` |
| `FeeSweepOperatorUpdated(address)` | `0xae994ca926e252e299c3df7516cb609272a57bf80b0e0715297e55939f873420` |
| `HookFeeBpsUpdated(uint256)` | `0xaea8b8d37d8110dd00c418d9c1c268f0fbadacb802c284b71a1777e411cd965a` |
| `HookFeeCollected(bytes32,address,uint256,uint256)` | `0xc532c43b3423e14ef72748f1c8291238829ca0af8ba9b67975ad1483485a4b4d` |
| `MaxInternalPriceImpactUpdated(uint256)` | `0x6968b68c1fb468c8b257b012290bf803a6a6d7e79468e0326050724f7573cf01` |
| `OwnershipTransferred(address,address)` | `0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0` |
| `OwnershipTransferStarted(address,address)` | `0x38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e22700` |
| `PoolConversionSkipped(bytes32,uint256)` | `0xeed2d18eb96f3c2cb8c7b6993512a506c170e17d29355f2d7a0d5961f338de09` |
| `PoolFeesRescued(bytes32,address,uint256,uint256)` | `0x0fbb28f9c335f55dcc5cc19e595ab55f9e6a0fd1b58ad77be3a98f99901daaff` |
| `PoolFeesSwept(bytes32,uint256,uint256)` | `0x2b33b68d948eb789fd57906bde3dc24d9f748c1df9b98d7359910f6aa1c06e2f` |
| `PoolRegistered(bytes32,address,address,address)` | `0x01bf263a1db1652580721573296e1a1fa70b3d4c87f61d02a69c4e1109d2d573` |
| `ProtocolFeeRecipientUpdated(address)` | `0xc1b5345cce283376356748dc57f2dfa7120431d016fc7ca9ba641bc65f91411d` |
| `ProtocolFeeShareUpdated(uint256)` | `0x4d1fc9430e27afb14db15169fd1c79e8b51773302919ac8c049f1c41995e380b` |
| `ReferralDiscountUpdated(uint256)` | `0xe0f45d08835a6839e8d2327d73ee817a6da7276c4776e77abe76eaa524bc92ef` |
| `ReferralFeeAccrued(bytes32,address,address,uint256)` | `0x7ca75a36687fd0a9628cbb8d737989015c0e5236d128a986b078ed14a030fe81` |
| `ReferralFeeClaimed(address,address,uint256)` | `0x646dbd2d0dbd68fc66a49d8c448dd995f308238033d47b3c6122f637b331bfdb` |
| `ReferralRegistrySet(address)` | `0xcf7381fd801bfc0e3e6a57a711e8165131a80c69051919ee96c9896cd87c0c11` |
| `ReferralShareUpdated(uint256)` | `0x7c13f976b8efb8331f00ce07146b8270d065e3789f2eabeab837c72ca942ad61` |


### Errors

| Error | Selector |
|---|---|
| `AlreadyRegistered()` | `0x3a81d6fc` |
| `AlreadySet()` | `0xa741a045` |
| `HookNotImplemented()` | `0x0a85dc29` |
| `InexactQuoteTransfer(address,uint256,uint256)` | `0x197001d6` |
| `InternalSwapRequiresOperator()` | `0x31cdb504` |
| `InvalidBps()` | `0xc6cc5d7f` |
| `InvalidPoolKey()` | `0xc256622b` |
| `MinimumOutputRequired()` | `0x3672d25f` |
| `NotFactory()` | `0x32cc7236` |
| `NotFeeSweepOperator()` | `0x8d42130c` |
| `NothingToRescue()` | `0x00f6b210` |
| `NotPoolManager()` | `0xae18210a` |
| `OwnableInvalidOwner(address)` | `0x1e4fbdf7` |
| `OwnableUnauthorizedAccount(address)` | `0x118cdaa7` |
| `OwnershipCannotBeRenounced()` | `0x2fab92ca` |
| `ReentrancyGuardReentrantCall()` | `0x3ee5aeb5` |
| `SafeCastOverflowedIntToUint(int256)` | `0xa8ce4432` |
| `SafeCastOverflowedUintToInt(uint256)` | `0x24775e06` |
| `SafeERC20FailedOperation(address)` | `0x5274afe7` |
| `SlippageExceeded(uint256,uint256)` | `0x71c4efed` |
| `UnknownPool()` | `0xf7139e33` |
| `ZeroAddress()` | `0xd92e233d` |

---
## FociFeeEscrow

`0x5a76a44B49ca0f7c4dB181f289C1eCA91d928406`


### Functions an application calls

### `balanceOf(address)`

`0x70a08231` · `view` · view

Returns the claimable native ETH balance for `recipient`.


**Parameters**

| Name | Type | Description |
|---|---|---|
| `recipient` | `address` |  |


**Returns**

| Name | Type | Description |
|---|---|---|
| `arg0` | `uint256` |  |

### `balanceOfToken(address,address)`

`0xf59e38b7` · `view` · view

Returns the claimable balance of `token` for `recipient`.

`(account, token)`. Read this rather than an indexer if you want on-chain truth for a claim button.


**Parameters**

| Name | Type | Description |
|---|---|---|
| `recipient` | `address` |  |
| `token` | `address` |  |


**Returns**

| Name | Type | Description |
|---|---|---|
| `arg0` | `uint256` |  |

### `claim()`

`0x4e71d92d` · `nonpayable` · Pull-only, `msg.sender`.

Pays out the caller's entire claimable native ETH balance.

Native-asset balance. Unreachable unless a launch quotes in the native asset, which no approved pair token does.


**Returns**

| Name | Type | Description |
|---|---|---|
| `amount` | `uint256` |  |

### `claimToken(address)`

`0x32f289cf` · `nonpayable` · Pull-only — claims the balance of `msg.sender`. You cannot claim for someone else.

Pays out the caller's entire claimable balance of `token`.

The escrow holds ONE balance per (recipient, token). It does not distinguish creator fees from referral fees — that split is attribution derived off-chain from events — so this withdraws both at once. Fees are in each launch's quote asset — an ERC-20 for every approved pair token — so this is the path, once per asset, not the native `claim()`.


**Parameters**

| Name | Type | Description |
|---|---|---|
| `token` | `address` |  |


**Returns**

| Name | Type | Description |
|---|---|---|
| `amount` | `uint256` |  |


**Reverts**

- `NoBalance` — nothing accrued for you in that token


### Other functions

| Signature | Selector | Mutability |
|---|---|---|
| `claim(uint256)` | `0x379607f5` | nonpayable |
| `claimToken(address,uint256)` | `0x1698755f` | nonpayable |
| `credit(address)` | `0xd5d44d80` | payable |
| `creditToken(address,address,uint256)` | `0x09ad4dd9` | nonpayable |


### Events

| Event | topic0 |
|---|---|
| `Claimed(address,uint256)` | `0xd8138f8a3f377c5259ca548e70e4c2de94f129f5a11036a15b69513cba2b426a` |
| `ClaimedToken(address,address,uint256)` | `0xdbc1ea3a8459e4c7e11fb385b52bbb5cc8c8ab85eec5d883ac9aa78c171f5141` |
| `Credited(address,address,uint256)` | `0x4e45da441832cf53bdaa69235704fc0575e68210f459ee1562911024b12967d5` |
| `CreditedToken(address,address,address,uint256)` | `0x5d104c62f50449fadfe6f4013c8f36588d32737f94b5ac9b83ddad33b3e1ffdf` |


### Errors

| Error | Selector |
|---|---|
| `InsufficientBalance(uint256,uint256)` | `0xcf479181` |
| `NoBalance()` | `0xc2caa2a6` |
| `ReentrancyGuardReentrantCall()` | `0x3ee5aeb5` |
| `SafeERC20FailedOperation(address)` | `0x5274afe7` |
| `TransferFailed()` | `0x90b8ec18` |
| `ZeroAddress()` | `0xd92e233d` |

---
## FociReferralRegistry

`0xe047D0F0ce0dD600732793762B1f1929Adc5015d`


### Functions an application calls

### `referrerOf(address)`

`0xd21cacdf` · `view` · view

The permanent referrer of each user, or the zero address if they have never been referred. Read by every curve on every trade.


**Parameters**

| Name | Type | Description |
|---|---|---|
| `user` | `address` |  |


**Returns**

| Name | Type | Description |
|---|---|---|
| `referrer` | `address` |  |

### `setReferrer(address)`

`0xa18a7bfc` · `nonpayable` · Self-service — binds `msg.sender`.

Claims a referrer for the caller before their first trade.

Reverts rather than no-ops on an existing binding: someone calling this directly asked for a specific outcome and should be told it did not happen, where a trade merely carrying a stale referrer should still settle.

Permanent and never rewritten. Bind before trading, or pass the referrer to `buy` instead.


**Parameters**

| Name | Type | Description |
|---|---|---|
| `referrer` | `address` |  |


**Reverts**

- `AlreadyReferred`
- `SelfReferral`
- `ReciprocalReferral`
- `ZeroAddress`


### Other functions

| Signature | Selector | Mutability |
|---|---|---|
| `bindFor(address,address)` | `0x620e206c` | nonpayable |
| `factory()` | `0xc45a0155` | view |
| `memeHook()` | `0x6651812c` | view |


### Events

| Event | topic0 |
|---|---|
| `ReferrerBound(address,address,address)` | `0x5b6dcb011725a9616ecced5408efb270f5e20477283b63e06c8b4eb0b4da4296` |


### Errors

| Error | Selector |
|---|---|
| `AlreadyReferred()` | `0x7aabdfe3` |
| `NotAuthorizedBinder()` | `0xbe447ef2` |
| `ReciprocalReferral()` | `0xb6ea0b01` |
| `SelfReferral()` | `0x55e8f70e` |
| `ZeroAddress()` | `0xd92e233d` |

---
## FociLaunchLocker

`0x539fD9e6a6316B65bEd9dDb9A570959e0bc8C31A`


### Other functions

| Signature | Selector | Mutability |
|---|---|---|
| `acceptOwnership()` | `0x79ba5097` | nonpayable |
| `factory()` | `0xc45a0155` | view |
| `isLocked(address)` | `0x4a4fbeec` | view |
| `lockedPositions(address)` | `0xfa22143d` | view |
| `lockedTokenSupply(address)` | `0x732e78e4` | view |
| `lockPosition(address,uint256)` | `0x292d5732` | nonpayable |
| `lockTokenSupply(address,uint256)` | `0xb8a0d7ab` | nonpayable |
| `onERC721Received(address,address,uint256,bytes)` | `0x150b7a02` | view |
| `owner()` | `0x8da5cb5b` | view |
| `pendingOwner()` | `0xe30c3978` | view |
| `positionManager()` | `0x791b98bc` | view |
| `renounceOwnership()` | `0x715018a6` | pure |
| `setFactory(address)` | `0x5bb47808` | nonpayable |
| `transferOwnership(address)` | `0xf2fde38b` | nonpayable |


### Events

| Event | topic0 |
|---|---|
| `FactorySet(address)` | `0x1edf3afd4ac789736e00d216cd88be164ddcef26a6eedcc30cdb0cb62f3741b1` |
| `OwnershipTransferred(address,address)` | `0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0` |
| `OwnershipTransferStarted(address,address)` | `0x38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e22700` |
| `PositionLocked(address,uint256)` | `0x2cabb2a2973327d5863ceb4707e9441851243897e86d587ee35943599752eb54` |
| `TokenSupplyLocked(address,uint256)` | `0xaf33c4aba92959b3e7ddc83ab728938262da159a6c05ca836f6c46f9bcb2c740` |


### Errors

| Error | Selector |
|---|---|
| `AlreadyInitialized()` | `0x0dc149f0` |
| `NotFactory()` | `0x32cc7236` |
| `NotPositionManager()` | `0x20fdc658` |
| `OwnableInvalidOwner(address)` | `0x1e4fbdf7` |
| `OwnableUnauthorizedAccount(address)` | `0x118cdaa7` |
| `OwnershipCannotBeRenounced()` | `0x2fab92ca` |
| `PositionAlreadyLocked()` | `0xfe3099b6` |
| `PositionNotHeld()` | `0x6b49c94a` |
| `SafeERC20FailedOperation(address)` | `0x5274afe7` |
| `ZeroAddress()` | `0xd92e233d` |

---
## FociLaunchDeployer

`0xa93f9CeFD92A77e1EAffa3246B6F4DB91a5c5659`


### Functions an application calls

### `predictLaunchAddresses((address,address,address,address,(address,uint16,uint16,uint16,uint16,uint16),address,uint256,uint256,uint256,uint256,uint256,bytes32,string,string,string,string,(string,string,string,string,string)))`

`0xe6a900b5` · `view` · view

Returns the addresses `deployLaunch` would produce for `params`, without deploying anything.

Lets a caller confirm that a launch it has not seen confirmed yet will land where it expects, and lets the launch path be checked for a salt the creator has already used. The token is derived from the curve because the curve's address is one of the token's constructor arguments, so the pair has to be computed in deployment order.

Computes the CREATE2 token and curve addresses before you send. Use it to mine a vanity address, and to check for an existing deployment — a reused salt reverts with the unhelpful `FailedDeployment`.


**Parameters**

| Name | Type | Description |
|---|---|---|
| `params` | `(address,address,address,address,(address,uint16,uint16,uint16,uint16,uint16),address,uint256,uint256,uint256,uint256,uint256,bytes32,string,string,string,string,(string,string,string,string,string))` |  |


**Returns**

| Name | Type | Description |
|---|---|---|
| `token` | `address` |  |
| `curve` | `address` |  |


### Other functions

| Signature | Selector | Mutability |
|---|---|---|
| `deployLaunch((address,address,address,address,(address,uint16,uint16,uint16,uint16,uint16),address,uint256,uint256,uint256,uint256,uint256,bytes32,string,string,string,string,(string,string,string,string,string)))` | `0x84b2d5c6` | nonpayable |
| `factory()` | `0xc45a0155` | view |
| `referralRegistry()` | `0x4e627e62` | view |


### Errors

| Error | Selector |
|---|---|
| `Create2EmptyBytecode()` | `0x4ca249dc` |
| `FailedDeployment()` | `0xb06ebf3d` |
| `InsufficientBalance(uint256,uint256)` | `0xcf479181` |
| `MetadataTooLong()` | `0x85b8e2f4` |
| `NotFactory()` | `0x32cc7236` |
| `ZeroAddress()` | `0xd92e233d` |

---
## FociGraduationExecutor

`0xEB286974C35d2741B0fe9b2a1Cd41E53d06aE406`


### Other functions

| Signature | Selector | Mutability |
|---|---|---|
| `factory()` | `0xc45a0155` | view |
| `locker()` | `0xd7b96d4e` | view |
| `mintFullRangePosition(address,(address,address,uint24,int24,address),int24,int24,uint160,uint256,uint256,address,address,address)` | `0xcbba1910` | payable |
| `permit2()` | `0x12261ee7` | view |
| `positionManager()` | `0x791b98bc` | view |


### Events

| Event | topic0 |
|---|---|
| `GraduationDustRetained(address,address,uint256)` | `0x667636bce2491e3f246c8b4ec1f4ca0be227dfa611d0575c59f5949283b433c1` |
| `GraduationDustSwept(address,address,uint256)` | `0x80a5a2ff8b8c5533e5862e4e161bbcade9af6fd9d67bef56a590b062107f027f` |


### Errors

| Error | Selector |
|---|---|
| `FeeTransferFailed()` | `0x4033e4e3` |
| `MintAmountOverflow()` | `0xeee66814` |
| `NotFactory()` | `0x32cc7236` |
| `SafeERC20FailedOperation(address)` | `0x5274afe7` |
| `SlippageExceeded(uint256,uint256)` | `0x71c4efed` |
| `ZeroAddress()` | `0xd92e233d` |

---
## FociRewardsDistributorFactory

`0xdac447110867954F00638125bbd5c66D8E0a7195`


### Functions an application calls

### `deploy(address,bytes32)`

`0x32c02a14` · `nonpayable` · Permissionless — every argument is checked against the launchpad's own record, so the caller cannot change the outcome. The keeper runs it minutes after a holder-rewards launch.

Deploys the distributor `token` already names as its creator fee recipient, and records the pair.

Permissionless: every argument is checked against the launchpad's own record, so who sends it cannot change the outcome. That matters because a creator's setup is only finished once this has run, and nothing should depend on them coming back to do it. Rejects a native quote because `harvest` only drains the escrow's ERC-20 ledger and a distributor has no `receive()` — such a launch could never be funded. This is a backstop, not the gate: by the time it runs the launch has already pointed its fees at an address that cannot use them. The real gate is refusing to offer holder fees on a native quote at all.

Deploys the distributor the launch already names as its `creatorFeeRecipient`, and records the pair in `distributorOf` / `tokenOf`. Re-attempting under the same salt is safe: the address is fixed, so the check either passes once or reverts.


**Parameters**

| Name | Type | Description |
|---|---|---|
| `token` | `address` |  |
| `salt` | `bytes32` |  |


**Returns**

| Name | Type | Description |
|---|---|---|
| `distributor` | `address` |  |


**Reverts**

- `TokenNotFound` — no such launch on the factory
- `NativeQuoteUnsupported` — the launch quotes in the native asset, which a distributor cannot harvest
- `AlreadyRegistered` — this token already has a distributor, or the address is already bound to another token
- `NotCreatorFeeRecipient(expected, actual)` — the launch's fee recipient is not `predict(deployer, salt, pairToken)`; the launch was made with a different salt, creator, or recipient

### `distributorOf(address)`

`0x3f20b9b4` · `view` · View, anyone.

Token → its deployed distributor, or zero. Zero for a launch that pays its creator, and for a holder-rewards launch whose `deploy` has not run yet.


**Parameters**

| Name | Type | Description |
|---|---|---|
| `token` | `address` |  |


**Returns**

| Name | Type | Description |
|---|---|---|
| `arg0` | `address` |  |

### `initCodeHash(address)`

`0x75fd9f28` · `view` · View, anyone.

Exposed so a prediction can be verified without this contract.

The creation-code hash `predict` uses, exposed so the prediction can be checked without this contract.


**Parameters**

| Name | Type | Description |
|---|---|---|
| `pairToken` | `address` |  |


**Returns**

| Name | Type | Description |
|---|---|---|
| `arg0` | `bytes32` |  |

### `predict(address,bytes32,address)`

`0x6339c853` · `view` · View, anyone.

The address `deploy` will produce for `creator` and `salt` on a launch quoted in `pairToken`.

Callers should read this rather than recomputing the salt rule, so the namespacing lives in exactly one place.

Where the distributor for `(creator, salt, pairToken)` will live. `creator` is the launch's `deployer` (the account that signs `launchToken`, or the account the router launches for) and `salt` is the same `TokenParams.salt` the launch uses — the address is a CREATE2 of `keccak256(abi.encode(creator, salt))`, namespaced per creator so nobody else's salt can land on it. Send the result as `TokenParams.creatorFeeRecipient` to make the launch pay its holders. Nothing exists there yet and nothing needs to: the escrow credits by address.


**Parameters**

| Name | Type | Description |
|---|---|---|
| `creator` | `address` |  |
| `salt` | `bytes32` |  |
| `pairToken` | `address` |  |


**Returns**

| Name | Type | Description |
|---|---|---|
| `arg0` | `address` |  |

### `tokenOf(address)`

`0x42ec38e2` · `view` · View, anyone.

Distributor → the token it was registered for. Informational only: the authoritative direction is the launch record's `creatorFeeRecipient`.


**Parameters**

| Name | Type | Description |
|---|---|---|
| `distributor` | `address` |  |


**Returns**

| Name | Type | Description |
|---|---|---|
| `arg0` | `address` |  |


### Other functions

| Signature | Selector | Mutability |
|---|---|---|
| `distributorOwner()` | `0x2127911d` | view |
| `escrow()` | `0xe2fdcc17` | view |
| `launchFactory()` | `0x536dac9b` | view |


### Events

| Event | topic0 |
|---|---|
| `DistributorDeployed(address,address,address,address,bytes32)` | `0x3ef9707063aece3a59b74cf57d0c1ed7a69cceb8630a1b4d42bd1a3f6743f560` |


### Errors

| Error | Selector |
|---|---|
| `AlreadyRegistered()` | `0x3a81d6fc` |
| `Create2EmptyBytecode()` | `0x4ca249dc` |
| `FailedDeployment()` | `0xb06ebf3d` |
| `InsufficientBalance(uint256,uint256)` | `0xcf479181` |
| `NativeQuoteUnsupported()` | `0x07609e34` |
| `NotCreatorFeeRecipient(address,address)` | `0x02f5a580` |
| `TokenNotFound()` | `0xcbdb7b30` |
| `ZeroAddress()` | `0xd92e233d` |

---
## FociRewardsDistributor

Deployed **once per launch** — no fixed address. Read it from `factory.getLaunchedToken(token)`.


### Functions an application calls

### `batchClaim(uint256[],address,uint256[],uint256[],bytes32[][])`

`0xa12e01d4` · `nonpayable` · Permissionless to submit; the proof fixes the recipient.

Settles several epochs for one account in a single transaction.

A holder accumulates one open epoch per distribution, so by the time they get around to claiming there are usually several. Sending N transactions for what is one decision is a bad enough experience that dust goes unclaimed and rolls over. Deliberately all-or-nothing: a bad proof reverts the batch rather than being skipped. Silently dropping one entry would let a caller believe an epoch was settled when it was not, and `hasClaimed` is the only record.

`claim` for several epochs in one transaction — a holder accumulates one open epoch per weekly distribution. Same checks per element; the whole batch reverts if any fails.


**Parameters**

| Name | Type | Description |
|---|---|---|
| `epochIds` | `uint256[]` |  |
| `account` | `address` |  |
| `amounts` | `uint256[]` |  |
| `indexes` | `uint256[]` |  |
| `proofs` | `bytes32[][]` |  |


**Reverts**

- `LengthMismatch` — the four arrays differ in length
- …then everything `claim` throws, per epoch

### `claim(uint256,address,uint256,uint256,bytes32[])`

`0x3e4fcb21` · `nonpayable` · Permissionless to submit; the proof fixes the recipient. Pays `account`, never `msg.sender`.

Claims `amount` from `epochId` for `account`.

Permissionless in who submits it: the proof fixes the recipient, so a third party can settle on a holder's behalf without being able to redirect anything.

Claims one epoch. `amount`, `index` and `proof` come from the published tree — the API returns them under `claimable.holders.byToken[].epochs[].calldata`, or rebuild the tree from chain data. Leaves are `keccak256(abi.encode(index, account, amount))` (`leafFor` returns the exact encoding); internal nodes hash sorted pairs.


**Parameters**

| Name | Type | Description |
|---|---|---|
| `epochId` | `uint256` |  |
| `account` | `address` |  |
| `amount` | `uint256` |  |
| `index` | `uint256` |  |
| `proof` | `bytes32[]` |  |


**Reverts**

- `NoEpoch` — no such epoch id
- `ClaimWindowClosed` — more than 90 days since the epoch was published; the funds have rolled over
- `ExcludedAccount` — `account` is on the exclusion list (the curve, the pool, the locker, the distributor itself, …)
- `AlreadyClaimed` — this `(epoch, account)` already claimed
- `InvalidProof` — leaf or proof does not match the epoch's root
- `EpochOverdrawn(total, claimed, amount)` — the tree over-allocated this epoch; cannot happen for a correctly built tree

### `epochCount()`

`0x829965cc` · `view` · View, anyone.

Epoch ids are `0 … epochCount - 1`.


**Returns**

| Name | Type | Description |
|---|---|---|
| `arg0` | `uint256` |  |

### `getEpoch(uint256)`

`0xbc0bc6ba` · `view` · View, anyone.

Root, total, claimed so far, publication time and expiry for an epoch.


**Parameters**

| Name | Type | Description |
|---|---|---|
| `epochId` | `uint256` |  |


**Returns**

| Name | Type | Description |
|---|---|---|
| `arg0` | `(bytes32,uint256,uint256,uint64,bool)` |  |

### `harvest()`

`0x4641257d` · `nonpayable` · Permissionless. The keeper calls it once at least about $1 of the launch's quote asset is waiting in the escrow.

Pulls this contract's accrued fees out of the launchpad's escrow.

Permissionless: the destination and amount are fixed by the escrow's ledger, so who triggers it does not matter. Credits the balance delta rather than the escrow's reported figure, so a quote asset that under- delivers can never make this contract believe it holds more than it does.

Pulls the distributor's escrow balance into `unallocated`, from where epochs are published. Returns the amount moved.


**Returns**

| Name | Type | Description |
|---|---|---|
| `amount` | `uint256` |  |


**Reverts**

- `NothingToHarvest` — the escrow holds nothing for this distributor

### `hasClaimed(uint256,address)`

`0x873f6f9e` · `view` · View, anyone.


**Parameters**

| Name | Type | Description |
|---|---|---|
| `epochId` | `uint256` |  |
| `account` | `address` |  |


**Returns**

| Name | Type | Description |
|---|---|---|
| `arg0` | `bool` |  |

### `isExcluded(address)`

`0xcba0e996` · `view` · View, anyone.

Addresses that can never receive a distribution, whatever a published root says. The pool above all: its liquidity is locked forever, so anything sent there is destroyed rather than distributed.

True for the distributor itself from construction (it self-excludes), and for anything the owner has excluded since.


**Parameters**

| Name | Type | Description |
|---|---|---|
| `account` | `address` |  |


**Returns**

| Name | Type | Description |
|---|---|---|
| `arg0` | `bool` |  |

### `leafFor(uint256,address,uint256)`

`0x98cd4d42` · `pure` · Pure, anyone.

The leaf a claim must prove, so callers and tree builders agree on the encoding rather than each guessing it.

The exact leaf encoding, `keccak256(abi.encode(index, account, amount))`, so a tree builder and the contract cannot disagree. Not `encodePacked`, not double-hashed.


**Parameters**

| Name | Type | Description |
|---|---|---|
| `index` | `uint256` |  |
| `account` | `address` |  |
| `amount` | `uint256` |  |


**Returns**

| Name | Type | Description |
|---|---|---|
| `arg0` | `bytes32` |  |

### `outstanding(uint256)`

`0x874a0e73` · `view` · View, anyone.

What an epoch still owes, whether or not its window has closed.

`total - claimed` for an epoch — what `rollOver` will return once the window closes.


**Parameters**

| Name | Type | Description |
|---|---|---|
| `epochId` | `uint256` |  |


**Returns**

| Name | Type | Description |
|---|---|---|
| `arg0` | `uint256` |  |

### `rollOver(uint256)`

`0xd27411ce` · `nonpayable` · Permissionless. The keeper calls it once an epoch's window has closed.

Returns an expired epoch's unclaimed remainder to the unallocated pool, so it funds a later distribution instead of being stranded.

Permissionless once the window has closed. There is no path that sends it anywhere but back into the next epoch, so nobody needs to be trusted to run it.

Returns an expired epoch's unclaimed remainder to `unallocated`, so a later epoch distributes it. Emits `RolledOver` even when the remainder is zero.


**Parameters**

| Name | Type | Description |
|---|---|---|
| `epochId` | `uint256` |  |


**Returns**

| Name | Type | Description |
|---|---|---|
| `amount` | `uint256` |  |


**Reverts**

- `NoEpoch` — no such epoch id
- `ClaimWindowOpen` — the 90-day window has not closed
- `AlreadyRolledOver` — already rolled

### `unallocated()`

`0xdf1c455c` · `view` · View, anyone.

Harvested but not yet committed to an epoch. Publishing draws from this, and a rolled-over epoch returns to it.

Harvested funds not yet committed to an epoch — what the next epoch can distribute.


**Returns**

| Name | Type | Description |
|---|---|---|
| `arg0` | `uint256` |  |


### Other functions

| Signature | Selector | Mutability |
|---|---|---|
| `acceptOwnership()` | `0x79ba5097` | nonpayable |
| `CLAIM_WINDOW()` | `0x9f34fc80` | view |
| `escrow()` | `0xe2fdcc17` | view |
| `owner()` | `0x8da5cb5b` | view |
| `pendingOwner()` | `0xe30c3978` | view |
| `publishEpoch(bytes32,uint256)` | `0xc16f10ee` | nonpayable |
| `quoteToken()` | `0x217a4b70` | view |
| `renounceOwnership()` | `0x715018a6` | pure |
| `setExcluded(address,bool)` | `0x2836be24` | nonpayable |
| `setExcludedBatch(address[],bool)` | `0x8018135d` | nonpayable |
| `transferOwnership(address)` | `0xf2fde38b` | nonpayable |


### Events

| Event | topic0 |
|---|---|
| `Claimed(uint256,address,uint256)` | `0x4ec90e965519d92681267467f775ada5bd214aa92c0dc93d90a5e880ce9ed026` |
| `EpochPublished(uint256,bytes32,uint256,uint64)` | `0x92c85369eead3d8555a7298769ddafde22d0f2f5f1ec05b2aa4059520533f003` |
| `ExclusionUpdated(address,bool)` | `0x83f2b279b6151af5a15cdbe8471d34fe8b34aed9bd9514ffeadb57c9ea366e61` |
| `Harvested(uint256,uint256)` | `0xfa07446fad45314351eb89109a154880278451332bb87f1824d435fe58da5939` |
| `OwnershipTransferred(address,address)` | `0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0` |
| `OwnershipTransferStarted(address,address)` | `0x38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e22700` |
| `RolledOver(uint256,uint256)` | `0xf52162a5e697efbc1377904d60df6a1b984cc85ca7964998e201cbea0b8e7549` |


### Errors

| Error | Selector |
|---|---|
| `AlreadyClaimed()` | `0x646cf558` |
| `AlreadyRolledOver()` | `0xd4b3db41` |
| `ClaimWindowClosed()` | `0xf0f25a33` |
| `ClaimWindowOpen()` | `0x29dfa3ce` |
| `EpochOverdrawn(uint256,uint256,uint256)` | `0x1e8a6f77` |
| `ExcludedAccount()` | `0xb7594bec` |
| `InsufficientUnallocated(uint256,uint256)` | `0x30e44a1d` |
| `InvalidProof()` | `0x09bde339` |
| `LengthMismatch()` | `0xff633a38` |
| `NoEpoch()` | `0x6a0b56a8` |
| `NothingToHarvest()` | `0x3f29331a` |
| `OwnableInvalidOwner(address)` | `0x1e4fbdf7` |
| `OwnableUnauthorizedAccount(address)` | `0x118cdaa7` |
| `OwnershipCannotBeRenounced()` | `0x2fab92ca` |
| `ReentrancyGuardReentrantCall()` | `0x3ee5aeb5` |
| `SafeERC20FailedOperation(address)` | `0x5274afe7` |
| `ZeroAddress()` | `0xd92e233d` |
| `ZeroAmount()` | `0x1f2a2005` |
