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); // onlyAdmin

There 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 initialize happen 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 Pool implementation, 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, a LISTING-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_BIT on any leg, including a foreign pool’s; the owner releases it at once, the seat after the LISTING notice.

What you do control, all self-serve once you hold the seat:

  • The base token and the initial tokens discovery list, agreed with the protocol owner at deploy.
  • protoSharePct, flashFeePbps and flowCooldownSecs, at deploy and after via UPDATE_FEES.
  • Asset listing, risk and lane config, hooks, treasury rotation and sealBootstrap, all through Admin, gated on your seat.
  • Handing the seat to someone else: requestOp(pool, UPDATE_POOL_ADMIN, ...) at the GOVERNANCE tier, accepted by the named address via acceptPoolAdmin(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:

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