Integration Guide · BNB Smart Chain

COOK Launchpad — Integration Guide for Routers, Aggregators & Trading Terminals

For partners routing order flow into COOK and charging their own fee on top. Everything below is taken from the deployed contracts, not from a spec.

01Quick facts

Chain
BNB Smart Chain (56)
COOKFactory
0x48b9DD955389e7d2d12A0996789ecFC188B9785b
COOKToken implementation
0x84aD306cf305ef080f793D4F64B74D58cB2077Ae
Token standard
ERC-20, 18 decimals, fixed 1,000,000,000 supply, no mint
Protocol trade fee
1% (100 bps) on the bonding curve
Post-graduation tax
1% on swaps against the PancakeSwap pair
Graduation venue
PancakeSwap V2, LP burned to 0x…dEaD

02Two phases — route differently

text
BONDING                                GRADUATED
────────────────────────────────       ────────────────────────────────
Venue : COOKFactory bonding curve      Venue : PancakeSwap V2 pair
Buy   : factory.buyFor()               Buy   : router, fee-on-transfer variant
Sell  : factory.sellFrom()             Sell  : router, fee-on-transfer variant
Token : TRANSFER-LOCKED                Token : freely transferable, 1% swap tax

Detect with factory.getToken(token).graduated. Graduation happens automatically inside the buy that fills the curve — same transaction, no separate listing step, no window to snipe the listing itself.

Calling buy() / sell() on the factory after graduation reverts AlreadyGraduated (0xe6a0d45f). Calling the PancakeSwap pair before graduation is impossible — the pair does not exist yet.

03The constraint that breaks naive integrations

During bonding, the token cannot be held by your contract.

COOKToken._transfer rejects any transfer where neither side is the factory, reverting TransfersLocked (0xdb89e3f4). This is deliberate — it is what stops anyone seeding an AMM pool or dumping OTC before the curve completes.

The API is built so you never need custody. Every path has the factory on one side:

text
✅ BUY : you send BNB → factory mints/transfers straight to the end user
✅ SELL: user approves the FACTORY → factory pulls straight from the user

After graduation the token is a normal (taxed) ERC-20 and you can route it however you like.

04Buying — buyFor

solidity
function buyFor(
    address token,
    address recipient,      // end user; tokens go here directly
    uint256 minTokensOut,
    uint256 deadline        // unix seconds, 0 = no deadline
) external payable returns (uint256 tokensOut);

Permissionless — no allowlist needed.

Taking your fee: skim the BNB before forwarding. The factory only ever sees the net amount, so your fee never touches the curve accounting.

solidity
function buyWithFee(address token, address user) external payable {
    uint256 fee = msg.value * 25 / 10_000;          // your 0.25%
    payable(feeCollector).transfer(fee);
    factory.buyFor{value: msg.value - fee}(token, user, minOut, deadline);
}
  • Tokens never pass through you. recipient receives them directly from the factory.
  • Overfill refunds go to recipient, not to the caller. If the order is larger than the remaining curve inventory, the factory spends only what it needs and refunds the rest to recipient. Your contract will not receive stray BNB, so it does not need a receive() for this path.

05Selling — sellFrom (allowlist required)

solidity
function sellFrom(
    address token,
    address owner,          // the end user whose tokens are sold
    uint256 tokenAmount,
    uint256 minBnbOut,
    address recipient,      // where the BNB lands — you, or the user
    uint256 deadline
) external returns (uint256 bnbOut);

The user approves the factory, not you. The factory then pulls owner → factory, which the transfer lock permits.

solidity
// 1) user (off-chain, their own wallet): token.approve(FACTORY, exactAmount)
// 2) you:
function sellWithFee(address token, address user, uint256 amount) external {
    uint256 before = address(this).balance;
    factory.sellFrom(token, user, amount, minOut, address(this), deadline);
    uint256 gross = address(this).balance - before;

    uint256 fee = gross * 25 / 10_000;              // your 0.25%
    payable(feeCollector).transfer(fee);
    payable(user).transfer(gross - fee);
}

Your contract needs a receive() external payable for this path.

Getting allowlisted

sellFrom is gated by isAllowedRouter[msg.sender], set by the COOK operator/owner via setAllowedRouter. Unlisted callers revert NotOperator (0x7c214f04).

Contact the COOK team with your router address to be added. Expect scrutiny: a cleared router picks recipient, minBnbOut and the timing, so it can in principle redirect the full proceeds of anyone holding an open approval. We ask integrators to:

  1. Use exact-amount approvals in your UI — never type(uint256).max on the factory.
  2. Set recipient to the end user where your flow allows it.
  3. Keep the router contract immutable or behind a timelock.

If you would rather not be allowlisted, you can still route sells by having the user call sell() directly and charging your fee off-chain or on the buy side only.

06Quoting

solidity
function getBuyQuote(address token, uint256 bnbIn)
    external view returns (uint256 tokensOut, uint256 fee, uint256 refund);

function getSellQuote(address token, uint256 tokenAmount)
    external view returns (uint256 bnbOut, uint256 fee);

These run the exact same math as buy / sell, so quoted == executed absent other traffic in between. fee is the 1% protocol fee in BNB; getSellQuote's bnbOut is already net of it.

Both revert AlreadyGraduated for graduated tokens — check graduated first, or catch the selector and fall through to your PancakeSwap quoting path.

Remember to subtract your own fee from what you display, since the factory has no knowledge of it.

Curve mechanics, if you want to quote locally

Constant product with virtual reserves:

text
k  = x0 · y0
x  = x0 + bnbRaised
y  = y0 − tokensSold
y0 = 1,066,666,666.666…      (VIRTUAL_TOKEN_RESERVE, larger than real supply on purpose)
x0 = raiseTarget / 3

Sold on the curve: 800,000,000 (80%). Paired into the burned LP at graduation: 200,000,000 (20%) plus the entire raise. y0 = S²/(S−L) is chosen so the curve's closing price equals the pool's opening price — there is no price step at graduation for you to arbitrage or to explain to users.

07After graduation — PancakeSwap V2

Standard pair, with one hard requirement:

You must use the …SupportingFeeOnTransferTokens router variants. The token takes 1% on swaps against the pair, so swapExactETHForTokens / swapExactTokensForETH will revert. Set slippage tolerance to at least 1.5%.

Wallet-to-wallet transfers are not taxed. Only transfers where the pair is the counterparty.

08Displaying the tax correctly

The nominal tax is exactly 1%, both directions. A sell measured with no swap-back pending costs 94 bps — the 1% tax, with rounding.

The variance comes from the swap-back. The token withholds tax in tokens. When the buffer reaches swapThreshold (0.05% of supply = 500,000), the next sell first swaps that buffer to BNB, and only then executes the user's own sell. The buffer's price impact therefore lands inside that one transaction.

09Error selectors

text
Factory
  0x025dbdd4  InsufficientFee        msg.value < creationFee()
  0x37c6d202  InitialBuyTooLarge     launch buy exceeds 5% of supply
  0x8199f5f3  SlippageExceeded       minTokensOut / minBnbOut not met
  0x70f65caa  DeadlinePassed         past deadline
  0xe6a0d45f  AlreadyGraduated       token has moved to PancakeSwap
  0x8698bf37  UnknownToken           not created by this factory
  0x7c214f04  NotOperator            caller is not an allowlisted router
  0x1f2a2005  ZeroAmount
  0xd92e233d  ZeroAddress

Token
  0xdb89e3f4  TransfersLocked        transfer attempted during bonding
  0x13be252b  InsufficientAllowance  user has not approved the factory
  0xf4d678b8  InsufficientBalance

10Events for indexing

text
TokenCreated  0x91de26bc430b3a4f1d6cfb11d72f2e5ca75d7622d37b2a88a8998ec28e747a11
Trade         0xf4682780a0e17045675b1fdf80687447d8cc9589bc87f718bcba8cbd53a2e81e
Graduated     0x487dc7f66c623fb0ff13f9024a3ff9675453d069e075eceb12d9f8d7870e2374
solidity
event Trade(
    address indexed token,
    address indexed trader,      // your router, when you route
    address indexed recipient,   // the end user
    bool    isBuy,
    uint256 bnbAmount,
    uint256 tokenAmount,
    uint256 fee,
    uint256 bnbReserve,          // curve state AFTER this trade
    uint256 tokensSold
);