White Labeling
A white-label instance is a pool the protocol owner deploys for you from the canonical PoolFactory on a supported chain, naming your address as the pool’s administrator (§2), and which you front with your own interface. Pool deployment itself is owner-gated: createPool is onlyAdmin and resolves to the chain’s single AccessControl.owner(), because every pool shares one CREATE3 namespace (the factory is the CREATE3 sender), so any other caller could spend a mined address reservation on a pool of its own. Everything after creation is yours.
Sharing an implementation and a factory has two structural consequences: a fleet upgrade you cannot opt out of (§1.1), and absence from the official routing index (§4). Deployment mechanics and the full curation surface: Pool Deployment & Curation §7.8. Chains and addresses: deployed instances.
1. Deployment
Each instance is a PoolProxy (ERC-1967; not a beacon proxy) of that chain’s canonical Pool implementation, deployed and initialized atomically:
function createPool(
bytes32 salt, // the mined address reservation, owner-held
address baseToken, // anchor token, e.g. USDC
address[] calldata tokens, // discovery index; MUST be non-empty
bytes calldata initdata // encoded call to Pool.initialize
) external returns (address pool); // onlyAdminThere is no positional owner or curator parameter; the seat that administers the pool is named inside initdata instead (poolAdmin_, §2). Fee config goes inside initdata too:
IPool.FeeParams memory feeParams;
feeParams.protoSharePct = 25; // % of swap and flash spread to the pool's fee sink, 0-100
feeParams.flashFeePbps = 100; // flash fee in PBPS, 10 to 10000 (100 = 0.01%)
feeParams.flowCooldownSecs = 15; // anti-JIT lock on new LP shares, default 15s, range 1 to 300
bytes memory initdata = abi.encodeCall(Pool.initialize, (baseToken, wnative, feeParams, partnerAdmin, lpTag));
address pool = factory.createPool(salt, baseToken, tokens, initdata);The fourth initialize argument is the seat: the owner sets it to your address at creation, and that is the only point at which it is chosen for you (§2). The zero sentinel resolves to AccessControl.owner() and is how protocol pools are created. The fifth, lpTag, is the bytes32 receipt tag: one code letter [A-Z] in byte 0, bytes 1 to 3 NUL, then a 1 to 12 character name from [A-Za-z ], NUL-padded. tokens only registers the discovery index; assets are not tradeable until listed (§2).
- The address is
f(create3Factory, poolFactory, salt).predictPool(salt)returns it from the chain alone, before deployment, and the same salt lands on the same address on every chain of a fleet. Base token and token roster are mutable pool state, not address inputs, so a base migration or a listing never moves a pool. - Because the address is chain-independent, a correct call sent to the wrong chain hits a real pool there, not a dead address. Check the chain ID before signing.
- One factory per chain deploys and registers every pool on it (
factory.isPool,factory.getAllPoolsCount()). - Per-instance storage, shared and swappable code. Every pool’s implementation slot is written only by
PoolFactory. - Deploy and
initializehappen in one transaction, no front-runnable gap.
1.1. The upgrade you cannot opt out of
A fleet upgrade at that chain’s factory moves your pool too. One AccessControl.owner() transaction, after a 7-day GOVERNANCE-tier timelock 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 Admin.
2. You are the administrator, of your pool only
Every pool carries its own seat, Pool.poolAdmin(), bound to whatever address initdata named at creation (§1). That seat, not the protocol, administers the pool: listing an asset, changing a risk or oracle config, installing a hook, rotating the fee sink, sealing bootstrap, and halting or cancelling your own queued ops all route through Admin gated on Pool.poolAdmin(), not on the protocol’s AccessControl.owner(). A protocol pool is the special case: its seat is the zero sentinel, which resolves to AccessControl.owner(). Full lever list and gotchas: Pool Deployment & Curation §7.8.
What that self-administration does not reach:
- The shared implementation. Every pool on the chain runs the one
Poolimplementation, which carries the mark store; upgrading it is protocol-only and fleet-wide (§1.1). - The official routing index. Your pool is not a routing candidate regardless of who administers it (§4). Becoming official is an owner-only grant, not something a seat can request for itself.
- New store lanes. A leg’s marks come from a lane of the chain’s shared mark store. Registering a lane (
MarkStoreP8.register, aLISTING-tier owner op) and mapping a third-party feed id to it (PoolFactory.setFeedLane) are protocol-only (§5); listing against an already-registered lane is self-serve. - The guardian’s halt. A guardian may still raise
HALT_BITon any leg, including a foreign pool’s; the owner releases it at once, the seat after theLISTINGnotice.
What you do control, all self-serve once you hold the seat:
- The base token and the initial
tokensdiscovery list, agreed with the protocol owner at deploy. protoSharePct,flashFeePbpsandflowCooldownSecs, at deploy and after viaUPDATE_FEES.- Asset listing, risk and lane config, hooks, treasury rotation and
sealBootstrap, all throughAdmin, gated on your seat. - Handing the seat to someone else:
requestOp(pool, UPDATE_POOL_ADMIN, ...)at the GOVERNANCE tier, accepted by the named address viaacceptPoolAdmin(pool). Staging the zero address donates the pool back to protocol governance; a pool that is official cannot hand over its seat. - Your entire front end.
AccessControl.transferOwnership and renounceOwnership stay permanently disabled at the protocol level (FeatureDisabled(Resource.TRANSFER); its rotation: Admin §1), but that governs the chain-wide AccessControl.owner(), not your pool’s seat. Your pool’s administration moves the way described above.
3. Front end
SDK pool functions are free functions taking an EIP-1193 provider and a pool address (signatures):
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. Slippage tolerance bands by trade size: Basic Operations §7.
You control the interface, the branding and the 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 |
getOfficialPoolsForToken(token) | Official pools containing a token, the routing candidates |
registerTokens(tokens) is called by a pool when it lists new assets and is isClone-gated (permanent from creation, so a later de-listed pool can still register); integrators cannot call it.
A white-label pool is not a routing candidate. getOfficialPoolsForToken reads the official index only, and official status is an owner-only grant (requestOfficial), never derived from who created the pool. Your pool stays 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
Your own seat self-serves asset listing, risk and lane configuration, and hooks (§2). What still needs the protocol team: deploying the pool in the first place, registering a new store lane, going official, multi-pool coordination, and custom mark work. Bring the base token, the assets you want listed, the lanes backing them, and the address that should hold the seat.
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: deployment, listing, oracles, risk, curves
- Basic Operations · Hooks · Protocol Fee Collection