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.

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:

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:

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.


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; the listing payload and its fields are in §3.1.

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:

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.

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:

CallReturns
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.