BullshotFactory is the main entry point of the Bullshot Protocol. It deploys BCToken and BondingCurve contracts using ERC1167 minimal proxy cloning (gas-efficient), tracks all created tokens, and emits global Buy/Sell events forwarded from individual bonding curves.
Key Features:
| Name | Type | Value | Description |
|---|---|---|---|
FEE_DENOMINATOR |
uint16 |
1000 | Denominator for fee calculations (per-mille) |
| Name | Type | Visibility | Current Value | Description |
|---|---|---|---|---|
buyFeePercent |
uint8 |
public | 10 (= 1%) | Fee charged on buys. Expressed in per-mille. Added on top of amountIn |
sellFeePercent |
uint8 |
public | 10 (= 1%) | Fee charged on sells. Deducted from amountOut |
launchFeePercent |
uint8 |
public | set by owner | Fee taken from the ETH pool balance at the moment of DEX launch |
creationFeeAmount |
uint256 |
public | 150000000000000 wei (0.00015 BNB) | Fixed BNB fee required to create a new token |
feeRecipient |
address payable |
public | set by owner | Address receiving all protocol fees |
| Name | Type | Visibility | Description |
|---|---|---|---|
uniswapV2Router |
address |
public | PancakeSwap V2 Router address |
uniswapV2Factory |
address |
public | PancakeSwap V2 Factory address |
isBondingCurveAddress |
mapping(address => bool) |
public | Maps BondingCurve address → true if deployed by this factory |
tokens |
BCToken[] |
public | Array of all BCToken contracts ever created |
Emitted when a new token and bonding curve pair are deployed.
event Created(
address indexed bondingCurve,
address indexed token,
address indexed pair,
address creator
);
Parameters:
bondingCurve: Address of the cloned BondingCurvetoken: Address of the cloned BCTokenpair: PancakeSwap pair address at creation time (address(0) — set on first buy)creator: msg.sender who called createTokenEmitted when any user buys on any bonding curve managed by this factory.
event Buy(
address indexed bc,
address indexed token,
address indexed buyer,
uint amountIn,
uint amountOut
);
Parameters:
bc: The BondingCurve contract addresstoken: The BCToken addressbuyer: Buyer's wallet address (to parameter from the buy call)amountIn: BNB spent (excluding fee), in weiamountOut: Token amount received, in weiEmitted when any user sells on any bonding curve managed by this factory.
event Sell(
address indexed bc,
address indexed token,
address indexed seller,
uint amountIn,
uint amountOut
);
Parameters:
bc: The BondingCurve contract addresstoken: The BCToken addressseller: Seller's wallet address (msg.sender in the sell call)amountIn: Token amount sold, in weiamountOut: BNB received (after fee), in weifunction createToken(
string memory name,
string memory ticker,
uint256 initAmountIn
) external payable
Deploys a new BCToken and BondingCurve pair, then optionally performs an initial buy on behalf of the creator.
Parameters:
name: Token full name (e.g. "Bullshot")ticker: Token symbol (e.g. "BULL")initAmountIn: BNB amount in wei to spend on initial buy. Pass 0 to skip.msg.value formula:
msg.value = initAmountIn
+ (initAmountIn * buyFeePercent / FEE_DENOMINATOR)
+ creationFeeAmount
Example (no initial buy, 0.01 BNB creation fee):
const creationFee = await factory.creationFeeAmount();
await factory.createToken("Bullshot", "BULL", 0, { value: creationFee });
Example (0.5 BNB initial buy, 0.01 BNB creation fee, 1% buy fee):
const initAmountIn = ethers.utils.parseEther("0.5");
const buyFeePercent = await factory.buyFeePercent(); // e.g. 10
const FEE_DENOMINATOR = 1000;
const creationFee = await factory.creationFeeAmount();
const buyFee = initAmountIn.mul(buyFeePercent).div(FEE_DENOMINATOR);
const value = initAmountIn.add(buyFee).add(creationFee);
await factory.createToken("Bullshot", "BULL", initAmountIn, { value });
Effects:
creationFeeAmount BNB to feeRecipientBondingCurve and a new BCToken using ERC1167isBondingCurveAddresstokens arrayCreatedinitAmountIn > 0, calls bondingCurve.buy(...) with the remaining valuefunction getTokensLength() external view returns (uint256)
Returns the total number of tokens ever created through this factory.
function getTokensList(
uint256 startIndex,
uint256 count
) external view returns (BCToken[] memory)
Returns a paginated slice of the tokens array.
Parameters:
startIndex: First index to include (0-based)count: Number of elements to returnReturns: Array of BCToken addresses (may be shorter than count if end of array is reached)
Example:
// Get first 20 tokens
const tokens = await factory.getTokensList(0, 20);
// Get next 20 tokens
const tokens2 = await factory.getTokensList(20, 20);
function setFee(
uint256 creationFeeAmount_,
uint8 buyFeePercent_,
uint8 sellFeePercent_,
uint8 launchFeePercent_,
address payable feeRecipient_
) public onlyOwner
Updates all protocol fee parameters. Only callable by the contract owner.
Parameters:
creationFeeAmount_: New fixed creation fee in weibuyFeePercent_: New buy fee per-millesellFeePercent_: New sell fee per-millelaunchFeePercent_: New launch fee per-millefeeRecipient_: New fee recipient addressfactory.on("Created", (bondingCurve, token, pair, creator, event) => {
console.log("New token created!");
console.log("Token:", token);
console.log("BondingCurve:", bondingCurve);
console.log("Creator:", creator);
});
factory.on("Buy", (bc, token, buyer, amountIn, amountOut) => {
console.log(`Buy on ${token}: ${ethers.utils.formatEther(amountIn)} BNB → ${ethers.utils.formatEther(amountOut)} tokens`);
});
factory.on("Sell", (bc, token, seller, amountIn, amountOut) => {
console.log(`Sell on ${token}: ${ethers.utils.formatEther(amountIn)} tokens → ${ethers.utils.formatEther(amountOut)} BNB`);
});
const isBondingCurve = await factory.isBondingCurveAddress("0x...");
if (isBondingCurve) {
console.log("This address is a Bullshot BondingCurve");
}
FEE_DENOMINATOR = 1000. So buyFeePercent = 10 means 1%.creationFeeAmount is in wei and transferred to feeRecipient before the bonding curve is initialized.pair field in the Created event is always address(0). The actual PancakeSwap pair is created lazily on the first buy call.emitBuyEvent and emitSellEvent can only be called by registered bonding curve addresses (isBondingCurveAddress[msg.sender] == true).