# BondingCurve Contract API Documentation

## Overview

BondingCurve is the AMM pricing engine for the Bullshot Protocol. Each token created through BullshotFactory gets its own BondingCurve clone. Trading happens on the bonding curve until the ETH reserve crosses the `launchThreshold`, at which point all liquidity is migrated to PancakeSwap automatically.

**Key Features:**
- Constant-product AMM (x\*y=k) with virtual reserves to set a non-zero initial price
- Configurable buy/sell/launch fees (inherited from factory at deployment)
- Slippage protection via `amountOutMin` / deadline
- Price pre-calculation view functions
- Auto-launch to PancakeSwap when funding threshold is reached

---

## Bonding Curve Formula

The curve uses a constant-product formula with **virtual reserves**:

```
k = virtualEthReserve × virtualTokenReserve
```

**Initial state (at deployment):**

| Variable | Value |
|----------|-------|
| `virtualTokenReserve` | 1,000,000,000 ether |
| `tokenReserve` | 800,000,000 ether |
| `virtualEthReserve` | 6 ether |
| `correlation (k)` | 6,000,000,000 ether² |
| `launchThreshold` | 24 ether |

The initial price per token is approximately:
```
price = virtualEthReserve / virtualTokenReserve = 6 / 1,000,000,000 = 6e-9 BNB/token
```

---

## Constants

| Name | Type | Value | Description |
|------|------|-------|-------------|
| `FEE_DENOMINATOR` | `uint16` | 1000 | Per-mille denominator for fee calculations |

---

## State Variables

| Name | Type | Visibility | Description |
|------|------|------------|-------------|
| `totalSupply` | `uint256` | public | Fixed token total supply (1,000,000,000 ether) |
| `virtualTokenReserve` | `uint256` | public | Current virtual token reserve (decreases on buy, increases on sell) |
| `tokenReserve` | `uint256` | public | Real tokens remaining for sale on the curve |
| `virtualEthReserve` | `uint256` | public | Current virtual ETH reserve (increases on buy, decreases on sell) |
| `ethReserve` | `uint256` | public | Real BNB held by this contract |
| `correlation` | `uint256` | public | Constant k = virtualEthReserve × virtualTokenReserve |
| `launchThreshold` | `uint256` | public | ETH amount that triggers DEX launch (~24 BNB) |
| `buyFeePercent` | `uint8` | public | Buy fee per-mille (charged on top of `amountIn`) |
| `sellFeePercent` | `uint8` | public | Sell fee per-mille (deducted from `amountOut`) |
| `launchFeePercent` | `uint8` | public | Fee per-mille of the ETH pool taken at DEX launch |
| `feeRecipient` | `address payable` | public | Fee recipient address |
| `uniswapV2Factory` | `IUniswapV2Factory` | public | PancakeSwap factory |
| `uniswapV2Router` | `IUniswapV2Router02` | public | PancakeSwap router |
| `token` | `BCToken` | public | The BCToken associated with this bonding curve |
| `pair` | `address` | public | PancakeSwap LP pair address (address(0) until first buy or DEX launch) |
| `factory` | `address` | public | The BullshotFactory address |

---

## Structs

### Data

Returned by `getData()`.

| Field | Type | Description |
|-------|------|-------------|
| `totalSupply` | `uint256` | Total token supply |
| `tokenReserve` | `uint256` | Remaining tokens for sale |
| `virtualTokenReserve` | `uint256` | Current virtual token reserve |
| `ethReserve` | `uint256` | Real BNB held in the curve |
| `virtualEthReserve` | `uint256` | Current virtual ETH reserve |
| `launchThreshold` | `uint256` | BNB level that triggers DEX launch |
| `buyFeePercent` | `uint8` | Buy fee per-mille |
| `sellFeePercent` | `uint8` | Sell fee per-mille |
| `pair` | `address` | PancakeSwap pair address |
| `balance` | `uint256` | Token balance of the queried account |

---

## Events

### Buy

```solidity
event Buy(address indexed buyer, uint amountIn, uint amountOut);
```

**Note:** This event is emitted on the BondingCurve. The BullshotFactory also proxies this to its global `Buy` event.

### Sell

```solidity
event Sell(address indexed seller, uint amountIn, uint amountOut);
```

### Launch

Emitted when the bonding curve triggers DEX migration.

```solidity
event Launch(uint tokenAmount, uint ethAmount);
```

**Parameters:**
- `tokenAmount`: Tokens added to the PancakeSwap LP
- `ethAmount`: BNB added to the PancakeSwap LP (after launch fee)

---

## Public Functions

### 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 exactly equal `amountIn + buyFee`.

**Parameters:**
- `amountIn`: BNB amount to spend (wei), **excluding fee**
- `amountOutMin`: Minimum token amount to receive (slippage protection)
- `to`: Recipient address of the purchased tokens
- `deadline`: Unix timestamp — tx reverts if `block.timestamp > deadline`

**`msg.value` requirement:**
```
buyFee = amountIn * buyFeePercent / FEE_DENOMINATOR
msg.value = amountIn + buyFee
```

**Returns:** `amountOut` — tokens received in wei

**Pricing formula:**
```
newVirtualEthReserve  = virtualEthReserve + amountIn
newVirtualTokenReserve = k / newVirtualEthReserve
amountOut              = virtualTokenReserve - newVirtualTokenReserve
```

**Effects:**
1. Collects buy fee and sends to `feeRecipient`
2. Updates `virtualEthReserve` and `virtualTokenReserve`
3. Creates PancakeSwap pair if not yet created (lazily)
4. Transfers tokens to `to`
5. Emits `Buy` (via BullshotFactory)
6. If `ethReserve >= launchThreshold`, triggers DEX launch

**DEX Launch Sequence:**
1. Calls `token.launch()` — unlocks free transfers
2. Takes launch fee from ETH pool
3. Calls `uniswapV2Router.addLiquidityETH` — migrates everything to PancakeSwap
4. LP tokens are permanently locked (sent to `address(0)`)
5. Emits `Launch`

---

### sell

```solidity
function sell(
    uint256 amountIn,
    uint256 amountOutMin,
    uint256 deadline
) external returns (uint256 amountOut)
```

Sell tokens back to the bonding curve.

**Prerequisites:**
```solidity
token.approve(bondingCurveAddress, amountIn);
```

**Parameters:**
- `amountIn`: Token amount to sell (wei)
- `amountOutMin`: Minimum BNB to receive (slippage protection)
- `deadline`: Unix timestamp

**Returns:** `amountOut` — BNB received (after sell fee) in wei

**Pricing formula:**
```
newVirtualTokenReserve = virtualTokenReserve + amountIn
newVirtualEthReserve   = k / newVirtualTokenReserve
amountOut              = virtualEthReserve - newVirtualEthReserve
sellFee                = amountOut * sellFeePercent / FEE_DENOMINATOR
net BNB to seller      = amountOut - sellFee
```

---

### calcBuyExactIn

```solidity
function calcBuyExactIn(uint256 amountIn) public view returns (
    uint256 amountOut,
    uint256 amountInMax,
    uint256 amountOutMax
)
```

Simulates a buy: given a BNB amount, returns expected token output.

**Parameters:**
- `amountIn`: BNB to spend (wei), **excluding fee**

**Returns:**
- `amountOut`: Expected tokens received
- `amountInMax`: Max BNB that can be spent (if `amountOut` would exceed `tokenReserve`)
- `amountOutMax`: Max tokens available (= `tokenReserve` if curve would be drained)

---

### calcBuyExactOut

```solidity
function calcBuyExactOut(uint256 amountOut) public view returns (
    uint256 amountIn,
    uint256 amountOutMax
)
```

Simulates a buy: given a desired token amount, returns the BNB cost.

**Returns:**
- `amountIn`: BNB required to buy `amountOut` tokens
- `amountOutMax`: Capped to `tokenReserve` if `amountOut` exceeds it

---

### calcSellExactIn

```solidity
function calcSellExactIn(uint256 amountIn) public view returns (
    uint256 amountOut,
    uint256 amountInMax,
    uint256 amountOutMax
)
```

Simulates a sell: given a token amount, returns expected BNB output (after fee).

---

### calcSellExactOut

```solidity
function calcSellExactOut(uint256 amountOut) public view returns (
    uint256 amountIn,
    uint256 amountOutMax
)
```

Simulates a sell: given a desired BNB amount, returns the token cost required.

---

### getData

```solidity
function getData(address account) public view returns (Data memory data)
```

Returns a full state snapshot of the bonding curve plus the token balance of `account`.

---

### getPostLaunchPrice

```solidity
function getPostLaunchPrice() public view returns (uint256)
```

Returns the current token price in wei from PancakeSwap reserves. Only valid after DEX launch (`pair != address(0)`).

**Formula:**
```
if token0 == tokenAddress:
    price = reserve1 * 1e18 / reserve0
else:
    price = reserve0 * 1e18 / reserve1
```

---

## Example Usage

### Buy Tokens

```javascript
const bondingCurve = new ethers.Contract(bondingCurveAddress, BondingCurveABI, signer);

const amountIn = ethers.utils.parseEther("0.1"); // 0.1 BNB
const buyFeePercent = await bondingCurve.buyFeePercent();
const buyFee = amountIn.mul(buyFeePercent).div(1000);
const msgValue = amountIn.add(buyFee);

// Pre-calculate expected output
const [amountOut] = await bondingCurve.calcBuyExactIn(amountIn);
const slippage = amountOut.mul(95).div(100); // 5% slippage tolerance

const deadline = Math.floor(Date.now() / 1000) + 60; // 1 min
const tx = await bondingCurve.buy(amountIn, slippage, signerAddress, deadline, {
    value: msgValue
});
await tx.wait();
```

### Sell Tokens

```javascript
const token = new ethers.Contract(tokenAddress, BCTokenABI, signer);
const bondingCurve = new ethers.Contract(bondingCurveAddress, BondingCurveABI, signer);

const amountIn = ethers.utils.parseEther("1000000"); // 1M tokens

// Approve bondingCurve to spend tokens
await token.approve(bondingCurveAddress, amountIn);

// Pre-calculate expected BNB output (after fee)
const [amountOut] = await bondingCurve.calcSellExactIn(amountIn);
const slippage = amountOut.mul(95).div(100);

const deadline = Math.floor(Date.now() / 1000) + 60;
const tx = await bondingCurve.sell(amountIn, slippage, deadline);
await tx.wait();
```

### Get Full State

```javascript
const data = await bondingCurve.getData(userAddress);
console.log("Token reserve:", ethers.utils.formatEther(data.tokenReserve));
console.log("ETH reserve:", ethers.utils.formatEther(data.ethReserve));
console.log("Launch threshold:", ethers.utils.formatEther(data.launchThreshold));
console.log("User balance:", ethers.utils.formatEther(data.balance));
console.log("Launched:", data.pair !== ethers.constants.AddressZero);
```

---

## Important Notes

1. **msg.value precision**: `msg.value` must equal exactly `amountIn + (amountIn * buyFeePercent / 1000)`. Any mismatch causes `"Wrong value"` revert.
2. **DEX Launch is irreversible**: Once `ethReserve >= launchThreshold`, the launch sequence runs within the same `buy()` call and cannot be reversed.
3. **LP tokens burned**: At launch, LP tokens are sent to `address(0)` — liquidity is permanently locked.
4. **Sell after launch**: After DEX launch, `ethReserve = 0` so all sells on the bonding curve will revert. Users must trade on PancakeSwap directly.
5. **Virtual reserves**: The virtual reserves ensure a non-zero initial price. The real `tokenReserve` starts at 800M (80% of supply); the remaining 200M are reserved for the PancakeSwap LP.
