BullshotFactory contract on BscScan (2025-07-30)BondingCurve AMM pricing engineBCToken ERC20 token contractCreate Token guide with full flow and fee calculation detailsBullshotFactory.abi, BondingCurve.abi, BCToken.abiBullshotSample.js_, BullshotSample.solMain factory contract. Deploys bonding curves and tokens via ERC1167 minimal proxy cloning. Emits global Buy, Sell, and Created events.
Network: BSC (BNB Smart Chain)
ABI File: BullshotFactory.abi
| Name | Type | Visibility | Description |
|---|---|---|---|
FEE_DENOMINATOR |
uint16 |
public constant | Fee base denominator (1000) |
buyFeePercent |
uint8 |
public | Buy fee as per-mille of amountIn (e.g., 10 = 1%) |
sellFeePercent |
uint8 |
public | Sell fee as per-mille of amountOut |
launchFeePercent |
uint8 |
public | Fee taken from ETH pool at DEX launch |
creationFeeAmount |
uint256 |
public | Fixed BNB fee required to create a token (in wei) |
feeRecipient |
address payable |
public | Address that receives all protocol fees |
uniswapV2Router |
address |
public | PancakeSwap V2 Router address |
uniswapV2Factory |
address |
public | PancakeSwap V2 Factory address |
isBondingCurveAddress |
mapping(address => bool) |
public | Returns true if address is a BondingCurve deployed by this factory |
tokens |
BCToken[] |
public | Array of all tokens created through the factory |
Emitted when a new token and bonding curve are deployed.
event Created(
address indexed bondingCurve,
address indexed token,
address indexed pair,
address creator
);
Parameters:
bondingCurve: Address of the newly deployed BondingCurve contracttoken: Address of the newly deployed BCToken contractpair: PancakeSwap pair address (address(0) at creation time; set later on first buy)creator: Address of the token creatorEmitted when a user buys tokens on a bonding curve (forwarded from BondingCurve).
event Buy(
address indexed bc,
address indexed token,
address indexed buyer,
uint amountIn,
uint amountOut
);
Parameters:
bc: BondingCurve contract addresstoken: BCToken addressbuyer: Buyer's wallet addressamountIn: BNB amount spent (excluding fee)amountOut: Token amount receivedEmitted when a user sells tokens on a bonding curve (forwarded from BondingCurve).
event Sell(
address indexed bc,
address indexed token,
address indexed seller,
uint amountIn,
uint amountOut
);
Parameters:
bc: BondingCurve contract addresstoken: BCToken addressseller: Seller's wallet addressamountIn: Token amount soldamountOut: BNB amount received (after fee)function createToken(
string memory name,
string memory ticker,
uint256 initAmountIn
) external payable
Creates a new token and its bonding curve. Optionally makes an initial buy on behalf of the caller.
Parameters:
name: Full name of the token (e.g., "Bullshot")ticker: Token symbol/ticker (e.g., "BULL")initAmountIn: BNB amount (in wei) to spend on an initial buy. Pass 0 to skip.msg.value requirement:
msg.value = initAmountIn + (initAmountIn * buyFeePercent / FEE_DENOMINATOR) + creationFeeAmount
function getTokensLength() external view returns (uint256)
Returns the total number of tokens 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: Start index (0-based)count: Number of tokens to returnfunction setFee(
uint256 creationFeeAmount_,
uint8 buyFeePercent_,
uint8 sellFeePercent_,
uint8 launchFeePercent_,
address payable feeRecipient_
) public onlyOwner
Updates all fee parameters. Only callable by the contract owner.
Constant-product AMM (x*y=k) with virtual reserves powering pre-launch token trading. Automatically launches to PancakeSwap when the ETH reserve crosses launchThreshold.
ABI File: BondingCurve.abi
| Name | Type | Visibility | Description |
|---|---|---|---|
FEE_DENOMINATOR |
uint16 |
public constant | 1000 |
totalSupply |
uint256 |
public | Total token supply (1,000,000,000 ether) |
virtualTokenReserve |
uint256 |
public | Virtual token reserve used in k=x*y formula |
tokenReserve |
uint256 |
public | Real tokens available for sale (800,000,000 ether initial) |
virtualEthReserve |
uint256 |
public | Virtual ETH reserve used in k=x*y formula (6 ether initial) |
ethReserve |
uint256 |
public | Real BNB held in the bonding curve |
correlation |
uint256 |
public | k constant = virtualEthReserve × virtualTokenReserve |
launchThreshold |
uint256 |
public | BNB ethReserve level at which DEX launch is triggered |
buyFeePercent |
uint8 |
public | Buy fee per-mille |
sellFeePercent |
uint8 |
public | Sell fee per-mille |
launchFeePercent |
uint8 |
public | Launch fee per-mille of eth pool |
feeRecipient |
address payable |
public | Fee recipient address |
uniswapV2Factory |
IUniswapV2Factory |
public | PancakeSwap factory reference |
uniswapV2Router |
IUniswapV2Router02 |
public | PancakeSwap router reference |
token |
BCToken |
public | Associated BCToken |
pair |
address |
public | PancakeSwap pair address (set on first buy or after launch) |
factory |
address |
public | BullshotFactory address |
event Buy(address indexed buyer, uint amountIn, uint amountOut);
event Sell(address indexed seller, uint amountIn, uint amountOut);
Emitted when the bonding curve triggers DEX launch.
event Launch(uint tokenAmount, uint ethAmount);
function buy(
uint256 amountIn,
uint256 amountOutMin,
address to,
uint256 deadline
) external payable returns (uint256 amountOut)
Buy tokens from the bonding curve. msg.value must equal amountIn + buyFee.
Parameters:
amountIn: BNB amount to spend (wei)amountOutMin: Minimum token output (slippage protection)to: Recipient of the purchased tokensdeadline: Unix timestamp deadlinefunction sell(
uint256 amountIn,
uint256 amountOutMin,
uint256 deadline
) external returns (uint256 amountOut)
Sell tokens back to the bonding curve. Requires prior token.approve(bondingCurve, amountIn).
Parameters:
amountIn: Token amount to sell (wei)amountOutMin: Minimum BNB to receive (slippage protection)deadline: Unix timestamp deadlinefunction calcBuyExactIn(uint256 amountIn) public view returns (
uint256 amountOut,
uint256 amountInMax,
uint256 amountOutMax
)
Pre-calculates token output when spending a fixed BNB amount.
function calcBuyExactOut(uint256 amountOut) public view returns (
uint256 amountIn,
uint256 amountOutMax
)
Pre-calculates BNB cost to receive a fixed token amount.
function calcSellExactIn(uint256 amountIn) public view returns (
uint256 amountOut,
uint256 amountInMax,
uint256 amountOutMax
)
Pre-calculates BNB output when selling a fixed token amount.
function calcSellExactOut(uint256 amountOut) public view returns (
uint256 amountIn,
uint256 amountOutMax
)
Pre-calculates token amount needed to receive a fixed BNB output.
function getData(address account) public view returns (Data memory data)
Returns a snapshot of all bonding curve state variables plus the account's token balance.
struct Data {
uint256 totalSupply;
uint256 tokenReserve;
uint256 virtualTokenReserve;
uint256 ethReserve;
uint256 virtualEthReserve;
uint256 launchThreshold;
uint8 buyFeePercent;
uint8 sellFeePercent;
address pair;
uint256 balance;
}
function getPostLaunchPrice() public view returns (uint256)
Returns the current token price from PancakeSwap reserves after DEX launch.
ERC20 token deployed per bonding curve. Transfers to the PancakeSwap pair are blocked until the bonding curve triggers launch().
ABI File: BCToken.abi
| Name | Type | Visibility | Description |
|---|---|---|---|
name |
string |
public | Token name |
symbol |
string |
public | Token symbol |
decimals |
uint8 |
public constant | 18 |
totalSupply |
uint256 |
public | Total token supply |
launched |
bool |
public | true after DEX launch; enables free transfers |
balanceOf |
mapping(address => uint256) |
public | ERC20 balances |
allowance |
mapping(address => mapping(address => uint256)) |
public | ERC20 allowances |
Standard ERC20 methods: transfer, transferFrom, approve.
function launch() public
Called exclusively by the BondingCurve to unlock transfers to the PancakeSwap pair. Reverts with Forbidden() if called by any other address.
| Error | Description |
|---|---|
! amountIn > 0 |
amountIn must be greater than zero |
! amountOutMin >= tokenReserve |
amountOutMin exceeds available supply |
Wrong value |
msg.value does not equal amountIn + buyFee |
! amountOut >= amountOutMin |
Slippage: price moved, output below minimum |
Expired |
block.timestamp exceeded deadline |
| Error | Description |
|---|---|
! amountOutMin >= ethReserve |
amountOutMin exceeds available ETH in curve |
! amountOut >= amountOutMin |
Slippage: price moved, output below minimum |
! amountOut >= ethReserve |
ETH reserve insufficient for payout |
Expired |
block.timestamp exceeded deadline |
Allowance |
Insufficient token allowance granted to BondingCurve |
| Error | Description |
|---|---|
Wrong value |
msg.value must equal initAmountIn + fee + creationFee exactly |
BCToken token = BCToken(tokenAddress);
bool isLaunched = token.launched();
If launched == true, the token has graduated to PancakeSwap and bonding curve trading is closed.
Check the pair field on the BondingCurve. Once the launch triggers, a PancakeSwap LP pair is created and pair will return a non-zero address, and ethReserve will be 0.
The bonding curve uses a constant-product formula with virtual reserves:
k = virtualEthReserve × virtualTokenReserve
= 6 ether × 1,000,000,000 ether
Initial parameters:
virtualTokenReserve = 1,000,000,000 ethertokenReserve (real, for sale) = 800,000,000 ethervirtualEthReserve = 6 etherlaunchThreshold = k / (virtualTokenReserve - tokenReserve) - virtualEthReserve = 24 etherWhen ethReserve >= 24 BNB, the token automatically launches to PancakeSwap.