---
title: "White Labeling"
description: "Running a branded pool on BTR: what a deployer controls, and the two things they do not"
audience: tech
type: reference
status: live
lang: en
updated: "2026-08-30"
publish: true
---
# White Labeling

A white-label instance is a pool you deploy yourself from the canonical `PoolFactory` on a supported chain and front with your own interface. Deployment is permissionless (one `createPool` call, no approval), but it grants no administrative authority.

This page covers what a deployer actually controls, and the two structural consequences of sharing an implementation and a factory: a beacon upgrade you cannot opt out of, and absence from the official routing index. Deployment mechanics and the full curation surface are in [Pool Deployment & Curation](/docs/5-1-2-pool-deployment-curation).

Today the fleet runs on Arc testnet (chain id `5042002`); other chains land as they deploy.

---

## 1. Deployment

Each instance is an ERC-1967 beacon proxy of that chain's canonical `Pool` implementation, deployed and initialized atomically:

```solidity
function createPool(
    address baseToken,          // anchor token, e.g. USDC
    address[] calldata tokens,  // discovery index + deployment salt; MUST be non-empty
    bytes calldata initdata     // encoded call to Pool.initialize
) external returns (address pool);
```

There is no `owner` or curator parameter and no positional `FeeParams` argument. Fee config goes inside `initdata`:

```solidity
IPool.FeeParams memory feeParams;
feeParams.protoSharePct = 25;   // % of swap and flash spread to the pool's fee sink, 0-100
feeParams.flashFeePbps  = 5;    // flash fee in PBPS (5 = 0.0005%)

bytes memory initdata = abi.encodeCall(IPool.initialize, (baseToken, wnative, feeParams));
address pool = factory.createPool(baseToken, tokens, initdata);
```

`tokens` only registers the discovery index. Assets are not tradeable until listed through `Admin`, a governance action you cannot self-serve (§2).

- One factory per chain deploys and registers every pool on it (`factory.isPool`, `factory.getAllPoolsCount()`).
- Per-instance storage, **shared and swappable code**. Every pool reads its implementation from `PoolFactory`, which is the beacon.
- Deploy and `initialize` happen in one transaction, no front-runnable gap.

### 1.1. The upgrade you cannot opt out of

A beacon implementation swap at that chain's factory re-points **your** pool too. One `AccessControl.owner()` transaction, after a 7-day UPGRADE-tier delay you do not control, changes the code your users trade against. You cannot opt out or pin a version.

Watch `ReferencePoolUpgradeRequested` on `PoolFactory` for the seven days of notice. The owner or any guardian can cancel, and the op expires if left unexecuted past its grace window. See [Deployment & Upgrades §4.1](/docs/3-2-deployment-upgrades#41-pool-beacon-upgrade-poolfactory-7-day-timelock).

---

## 2. You are not the administrator

There is **no per-pool owner or curator role**. `Pool.owner()` proxies to the single `AccessControl.owner()` of its chain, and every admin-gated call, on your pool and on every other pool on that chain, resolves to that one address through the `Admin` singleton.

So a white-label deployer cannot list an asset, change a risk or oracle config, install a hook, set the fee sink, or halt a leg. Those are coordinated with the protocol team (§5). The complete authority and timelock table is in [Pool Deployment & Curation §2.2](/docs/5-1-2-pool-deployment-curation#22-ownership-model); the listing payload and its fields are in [§3.1](/docs/5-1-2-pool-deployment-curation#31-adding-an-asset).

What you do control:

- The base token.
- The initial `tokens` discovery list.
- `protoSharePct` and `flashFeePbps` at `initialize`.
- Your entire front end.

**Nor can that authority be handed to you.** `AccessControl.transferOwnership` and `renounceOwnership` are permanently disabled: both revert `FeatureDisabled(Resource.TRANSFER)`, and the owner itself cannot call them either. Ownership moves only through Solady's two-step handover (`requestOwnershipHandover` by the incoming owner, `completeOwnershipHandover` by the sitting one), it is a property of the chain-wide `AccessControl` singleton rather than of any pool, and a zero-target complete is rejected to close the back-door renounce. There is no path by which deploying a pool makes you its administrator.

---

## 3. Front end

Pool functions in the SDK are free functions taking an EIP-1193 provider and a pool address:

```typescript
import { getSwapQuote, swap, deposit, defaultDeadline } from '@btr-protocol/sdk/pool';

const quote = await getSwapQuote(provider, yourPool, tokenIn, tokenOut, amountIn);

// Apply a real tolerance. Passing quote.amountOut unmodified is a zero-slippage
// order that reverts on any adverse tick between quote and inclusion.
const minAmountOut = (quote.amountOut * 9950n) / 10000n; // 50 bps

const tx = await swap(provider, yourPool, {
  tokenIn, tokenOut, amountIn,
  minAmountOut,
  recipient: userAddress,
  deadline: defaultDeadline(),
});

const depositTx = await deposit(provider, yourPool, { token, amount });
```

Callers must approve `tokenIn` to the pool first. Tolerance bands by trade size: [Basic Operations §7](/docs/5-1-1-basic-operations#7-slippage-protection).

Your interface, your branding, your pool address. Keep a "Powered by BTR Protocol" attribution and link back to these docs.

---

## 4. Discovery and routing

Read-only, callable by anyone on that chain's `PoolFactory`:

| Call | Returns |
|---|---|
| `getPoolTokens(pool)` | Tokens registered in a pool |
| `getPoolsForToken(token)` | All pools containing a token |
| `isPool(addr)` / `isOfficialPool(addr)` | Membership |
| `getCommonPools(tokenA, tokenB)` | **Official** pools containing both, the routing candidates |
| `getOfficialPoolsForToken(token)` | **Official** pools containing a token |

`registerTokens(tokens)` is called *by* a pool when it lists new assets and is `isPool`-gated; integrators cannot call it.

**A white-label pool is not a routing candidate.** `getCommonPools` and `getOfficialPoolsForToken` read the official index only, which a pool joins when its creator is the factory's protocol deployer. Your pool is registered and queryable through `isPool`, `getPoolTokens` and `getPoolsForToken`, but the router will not find it. Address it directly from your own front end.

---

## 5. Working with the protocol team

Asset additions, risk and oracle configuration, hook installation, multi-pool coordination and custom oracle work all route through the AC owner. Bring the pool address, the assets you want listed, and the feeds backing them.

Do not modify pool logic: every pool on a chain executes the one shared implementation, and behavioural customization is limited to what is configurable above.

---

## 6. Related

- [Pool Deployment & Curation](/docs/5-1-2-pool-deployment-curation): deployment, listing, oracles, risk, curves
- [Basic Operations](/docs/5-1-1-basic-operations) · [Hooks](/docs/5-1-3-hooks) · [Protocol Fee Collection](/docs/5-3-2-protocol-fee-collection)
