# Table of Contents

- [CHANGES](#changes)
  - [25/03/2026](#changes---25032026)
- [Protocol Contracts](#protocol-contracts)
  - [BullshotFactory](#bullshotfactory)
    - [State Variables](#state-variables)
    - [Events](#events)
    - [Methods](#methods)
  - [BondingCurve](#bondingcurve)
    - [State Variables](#state-variables-1)
    - [Events](#events-1)
    - [Methods](#methods-1)
  - [BCToken](#bctoken)
    - [State Variables](#state-variables-2)
    - [Methods](#methods-2)
- [FAQ](#faq)
  - [Error Codes](#error-codes)
  - [How to Identify a Launched Token](#how-to-identify-a-launched-token)
  - [How Bonding Curve Pricing Works](#how-bonding-curve-pricing-works)

---

# CHANGES - 25/03/2026

## Initial Release
- Published `BullshotFactory` contract on BscScan (2025-07-30)
- Added documentation for `BondingCurve` AMM pricing engine
- Added documentation for `BCToken` ERC20 token contract
- Added `Create Token` guide with full flow and fee calculation details
- Published ABI files: `BullshotFactory.abi`, `BondingCurve.abi`, `BCToken.abi`
- Added integration sample files: `BullshotSample.js_`, `BullshotSample.sol`

---

# Protocol Contracts

## BullshotFactory

Main 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`

### State Variables

| 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 |

### Events

#### Created

Emitted when a new token and bonding curve are deployed.

```solidity
event Created(
    address indexed bondingCurve,
    address indexed token,
    address indexed pair,
    address creator
);
```

**Parameters:**
- `bondingCurve`: Address of the newly deployed BondingCurve contract
- `token`: Address of the newly deployed BCToken contract
- `pair`: PancakeSwap pair address (address(0) at creation time; set later on first buy)
- `creator`: Address of the token creator

#### Buy

Emitted when a user buys tokens on a bonding curve (forwarded from BondingCurve).

```solidity
event Buy(
    address indexed bc,
    address indexed token,
    address indexed buyer,
    uint amountIn,
    uint amountOut
);
```

**Parameters:**
- `bc`: BondingCurve contract address
- `token`: BCToken address
- `buyer`: Buyer's wallet address
- `amountIn`: BNB amount spent (excluding fee)
- `amountOut`: Token amount received

#### Sell

Emitted when a user sells tokens on a bonding curve (forwarded from BondingCurve).

```solidity
event Sell(
    address indexed bc,
    address indexed token,
    address indexed seller,
    uint amountIn,
    uint amountOut
);
```

**Parameters:**
- `bc`: BondingCurve contract address
- `token`: BCToken address
- `seller`: Seller's wallet address
- `amountIn`: Token amount sold
- `amountOut`: BNB amount received (after fee)

### Methods

#### createToken

```solidity
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
```

#### getTokensLength

```solidity
function getTokensLength() external view returns (uint256)
```

Returns the total number of tokens created through this factory.

#### getTokensList

```solidity
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 return

#### setFee

```solidity
function setFee(
    uint256 creationFeeAmount_,
    uint8 buyFeePercent_,
    uint8 sellFeePercent_,
    uint8 launchFeePercent_,
    address payable feeRecipient_
) public onlyOwner
```

Updates all fee parameters. Only callable by the contract owner.

---

## BondingCurve

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`

### State Variables

| 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 |

### Events

#### Buy

```solidity
event Buy(address indexed buyer, uint amountIn, uint amountOut);
```

#### Sell

```solidity
event Sell(address indexed seller, uint amountIn, uint amountOut);
```

#### Launch

Emitted when the bonding curve triggers DEX launch.

```solidity
event Launch(uint tokenAmount, uint ethAmount);
```

### Methods

#### buy

```solidity
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 tokens
- `deadline`: Unix timestamp deadline

#### sell

```solidity
function 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 deadline

#### calcBuyExactIn

```solidity
function calcBuyExactIn(uint256 amountIn) public view returns (
    uint256 amountOut,
    uint256 amountInMax,
    uint256 amountOutMax
)
```

Pre-calculates token output when spending a fixed BNB amount.

#### calcBuyExactOut

```solidity
function calcBuyExactOut(uint256 amountOut) public view returns (
    uint256 amountIn,
    uint256 amountOutMax
)
```

Pre-calculates BNB cost to receive a fixed token amount.

#### calcSellExactIn

```solidity
function calcSellExactIn(uint256 amountIn) public view returns (
    uint256 amountOut,
    uint256 amountInMax,
    uint256 amountOutMax
)
```

Pre-calculates BNB output when selling a fixed token amount.

#### calcSellExactOut

```solidity
function calcSellExactOut(uint256 amountOut) public view returns (
    uint256 amountIn,
    uint256 amountOutMax
)
```

Pre-calculates token amount needed to receive a fixed BNB output.

#### getData

```solidity
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.

```solidity
struct Data {
    uint256 totalSupply;
    uint256 tokenReserve;
    uint256 virtualTokenReserve;
    uint256 ethReserve;
    uint256 virtualEthReserve;
    uint256 launchThreshold;
    uint8 buyFeePercent;
    uint8 sellFeePercent;
    address pair;
    uint256 balance;
}
```

#### getPostLaunchPrice

```solidity
function getPostLaunchPrice() public view returns (uint256)
```

Returns the current token price from PancakeSwap reserves after DEX launch.

---

## BCToken

ERC20 token deployed per bonding curve. Transfers to the PancakeSwap pair are blocked until the bonding curve triggers `launch()`.

**ABI File:** `BCToken.abi`

### State Variables

| 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 |

### Methods

Standard ERC20 methods: `transfer`, `transferFrom`, `approve`.

#### launch

```solidity
function launch() public
```

Called exclusively by the BondingCurve to unlock transfers to the PancakeSwap pair. Reverts with `Forbidden()` if called by any other address.

---

# FAQ

## Error Codes

### buy Errors

| 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 |

### sell Errors

| 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 |

### createToken Errors

| Error | Description |
|-------|-------------|
| `Wrong value` | msg.value must equal initAmountIn + fee + creationFee exactly |

## How to Identify a Launched Token

### On-chain

```solidity
BCToken token = BCToken(tokenAddress);
bool isLaunched = token.launched();
```

If `launched == true`, the token has graduated to PancakeSwap and bonding curve trading is closed.

### Off-chain

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`.

## How Bonding Curve Pricing Works

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 ether
- `tokenReserve` (real, for sale) = 800,000,000 ether
- `virtualEthReserve` = 6 ether
- `launchThreshold` = k / (virtualTokenReserve - tokenReserve) - virtualEthReserve = **24 ether**

When `ethReserve >= 24 BNB`, the token automatically launches to PancakeSwap.
