Bonding Curve Trading

Before graduation, tokens trade exclusively through the Bonding Curve, with execution handled by IgnixManager rather than a DEX.

The curve uses the constant-product model x · y = k, but its fee calculation differs from a standard AMM.

Caution

There is no onchain quote function. minTokensOut and minQuoteOut must be calculated by the caller.

Reading Curve State

IgnixManager(manager).tokens(token)

Remaining amount available:

sellable - sold

Buy

Fees are deducted from the input amount before the remaining amount enters the curve:

feeBps = buyFeeBps + taxBuyBps + snipeBpsNow(token)

net = quoteIn − quoteIn × feeBps / 10000

out = vToken − ceil(vQuote × vToken / (vQuote + net))

snipeBpsNow(token) returns the current Anti-Snipe Tax and becomes 0 once the protection window ends.

// Native Quote Token
IgnixManager(manager).buy{value: amountIn}(
    token, amountIn, minTokensOut
)

// ERC20 Quote Token; approve first
IgnixManager(manager).buy(
    token, amountIn, minTokensOut
)

For native Quote Tokens, msg.value must equal amountIn.

Caution

The final buy may trigger a refund. If out exceeds the remaining available amount, only the remaining tokens are purchased. The unused input is refunded and no fee is charged on that portion. Use balance changes or emitted events to determine the actual amount spent.

Sell

Sell-side fees are deducted from the output amount:

gross = vQuote − ceil(vQuote × vToken / (vToken + tokenIn))

net = gross − gross × (sellFeeBps + taxSellBps) / 10000
IgnixManager(manager).sell(
    token, tokenIn, minQuoteOut
)

minQuoteOut corresponds to net, not gross.

Note

Buy fees are deducted from the input, while sell fees are deducted from the output. The two sides use different calculations.

Integrating from a contract

Aggregators, trading bots and other contracts can route through the curve, but the generic "swap into your own contract, then forward to the user" pattern does not work here.

The transfer restriction

Before graduation a launch token can only move to or from IgnixManager. Any other transfer reverts with CurveOnly() (0x9dabc49b). Graduation lifts the restriction permanently, after which the token is an ordinary ERC20 in a standard Uniswap pool.

The restriction exists so nobody can buy on the curve, spin up their own pool and sell at an arbitrary price into a market disconnected from the curve.

Note

The restriction applies to the launch token only. Quote Tokens (native OKB, wrapped xStocks, …) are unrestricted and can be routed through your contract freely — so multi-hop such as OKB → wNVDAx → launch token is possible. Only the hop that produces the launch token must deliver straight to the user.

Buy: buyTo

Callable by any address — no allowlist.

function buyTo(
    address token,
    uint256 amountIn,
    uint256 minTokensOut,
    address recipient      // the end user, not your own contract
) external payable;

The Manager pulls the Quote Token from msg.sender and sends the launch token and any near-graduation refund straight to recipient. Your contract never holds the launch token, so CurveOnly() cannot trigger.

Quote TokenPrerequisite
ERC20quote.approve(manager, amountIn) first; msg.value must be 0
Native (quote == address(0))msg.value == amountIn; no approval needed

Approve the Manager, not your own contract — it pulls via safeTransferFrom(msg.sender, …).

Sell: sellFrom

Callable by any address — no allowlist — but it requires an EIP-712 authorization from the seller, and the signature is bound to the calling contract.

function sellFrom(
    address token,
    address payer,          // the seller
    address recipient,      // Quote Token recipient; may be your own contract
    uint256 tokenIn,
    uint256 minQuoteOut,
    uint256 deadline,
    bytes calldata authorization
) external;

The launch token moves straight from payer to the Manager and the Quote Token straight to recipient. The seller must approve the Manager for the launch token, not your contract.

EIP-712 domain:

{ "name": "IgnixManager", "version": "1", "chainId": 196,
  "verifyingContract": "<IgnixManager address>" }
SellFrom(address adapter,address token,address payer,address recipient,uint256 tokenIn,uint256 minQuoteOut,uint256 nonce,uint256 deadline)

adapter is the contract that calls sellFrom; nonce is the current sellNonces(payer) and increments on every call, so each authorization is single-use. Signatures are checked with SignatureChecker, so both EOAs and ERC-1271 smart accounts work.

function DOMAIN_SEPARATOR() external view returns (bytes32);
function SELL_FROM_TYPEHASH() external pure returns (bytes32);
function sellNonces(address payer) external view returns (uint256);
function cancelSellAuthorizations() external;  // seller-invoked; voids all pending authorizations

Because the sell output is a Quote Token, routing it onward is unrestricted: set recipient to your own contract, swap, then pay the user in whatever asset you like.

sellFromOrigin

A signature-free sell entry point also exists, but it is restricted to addresses the platform has explicitly allowlisted; every other caller reverts with NotSellAdapter() (0x9d65dd00). Third-party integrations should use sellFrom.

Revert reference

SelectorErrorMeaning
0x9dabc49bCurveOnlyA launch token was sent to something other than the Manager — you are still forwarding through your own contract
0x5cd5d233BadSignatureAuthorization failed: check adapter, nonce, and that every field matches the call
0x0819bdcdSignatureExpiredPast deadline
0x0bba69fbBadValueZero/Manager recipient or payer; msg.value present for an ERC20 quote; msg.value != amountIn for a native quote
0x2c353d89FounderOnlyThe founder round is still open; public buys are closed
0x7dd37f70SlippageOutput below minTokensOut / minQuoteOut
0x52df9fe5SoldOutThe curve is exhausted
0x9e87fac8PausedPlatform pause gate is active

CurveOnly is declared on the token contract, not the Manager, so it will not decode against the Manager ABI — match it by selector.

Rounding

All divisions in the formulas above must be rounded up.

Rounding down may result in quoted output being slightly higher than the actual onchain output, causing minTokensOut or minQuoteOut to fail.

Graduation

When sold == sellable, the token graduates within the same transaction and liquidity is deployed to the DEX.

After graduation:

IgnixManager.buy()  → revert
IgnixManager.sell() → revert

Subsequent trading automatically moves to Uniswap. See Token Types for graduation detection.

Dividends

Holder dividends can be queried and claimed through the token's DividendTracker:

address tracker = IgnixToken(token).tracker();

IDividendTracker(tracker).allAssets();
IDividendTracker(tracker).withdrawableOf(holder, asset);
IDividendTracker(tracker).claimAll();
Caution

Dividends stream linearly over 24 hours, so withdrawableOf changes continuously and should not be cached for long periods.