# Buy Token

Purchases tokens from the bonding curve using SOL.

## Instruction INDEX

2

## Instruction

```rust
SCBondingCurveInstruction::BuyToken {
    pda_token_nonce: u8,
    sol_to_spend: u64,
    min_token_to_receive: u64,
    logging_account_bump: u8,
}
```

## Parameters

| Parameter              | Type  | Description                                                      |
| ---------------------- | ----- | ---------------------------------------------------------------- |
| `pda_token_nonce`      | `u8`  | Nonce for the token owner PDA                                    |
| `sol_to_spend`         | `u64` | Amount of SOL to spend in lamports                               |
| `min_token_to_receive` | `u64` | Minimum acceptable token amount to receive (slippage protection) |
| `logging_account_bump` | `u8`  | Bump for the logging authority PDA                               |

## Account Setup

| #  | Account               | Description                      | Derivation                                               |
| -- | --------------------- | -------------------------------- | -------------------------------------------------------- |
| 1  | Payer account         | Transaction signer (User wallet) | -                                                        |
| 2  | System program        | System program                   | `SystemProgram.programId`                                |
| 3  | Token program         | SPL Token program                | `TOKEN_PROGRAM_ID`                                       |
| 4  | Token owner PDA       | Bonding curve account            | `[Buffer.from("token_owner"), mint.toBuffer(), [nonce]]` |
| 5  | Token mint            | Token mint address               | Provided by user                                         |
| 6  | Owner token account   | Token account for PDA            | `getAssociatedTokenAddress(mint, ownerPDA, true)`        |
| 7  | User token account    | User's token account             | `getAssociatedTokenAddress(mint, userWallet, false)`     |
| 8  | Fee account           | Platform fee account             | Fixed address (see Reference section)                    |
| 9  | Properties account    | Platform configuration           | `[Buffer.from("settings")]`                              |
| 10 | Program account       | SC Bonding Curve program         | SC Bonding Curve program ID                              |
| 11 | Log authority account | For structured logs              | `[Buffer.from("logging_authority")]`                     |

## Function Logic

1. Verifies the PDA token owner matches the expected PDA for the token mint
2. Retrieves platform fee from properties account (currently 1%)
3. Calculates amount without fee (99% of sol\_to\_spend)
4. Retrieves current pool balances (virtual + real SOL and tokens)
5. Calculates expected token amount using the bonding curve formula
6. Verifies expected token amount meets minimum specified (slippage check)
7. Checks if purchase would finalize the token sale
8. If finalizing, adjusts amounts accordingly and marks the token as finalized
9. Transfers SOL to the bonding curve and fee account
10. Transfers tokens to the user
11. Updates pool balances in the bonding curve account
12. Records buy data in logs

## Price Calculation

```
token_amount = pool_token_balance * sol_amount / (pool_sol_balance + sol_amount)
```

Where:

* `pool_token_balance` = virtual\_token + real\_token
* `pool_sol_balance` = virtual\_sol + real\_sol
* `sol_amount` = amount of SOL after fee deduction

## Notes

* The platform fee is deducted from the SOL amount before calculating token amount
* If the purchase would leave less than POOL\_MIGRATION\_RESERVES tokens in the pool, the token sale is finalized
* A finalized token can no longer be traded on the bonding curve and is ready for migration

## Example Usage

```javascript
// Derive PDAs and other accounts
const [tokenOwnerPDA, pdaTokenNonce] = PublicKey.findProgramAddressSync(
  [Buffer.from("token_owner"), tokenMint.toBuffer()],
  SC_BONDING_CURVE_PROGRAM_ID
)

const [propertiesAccount] = PublicKey.findProgramAddressSync(
  [Buffer.from("settings")],
  SC_BONDING_CURVE_PROGRAM_ID
)

const [logAuthority, logAuthorityBump] = PublicKey.findProgramAddressSync(
  [Buffer.from("logging_authority")],
  SC_BONDING_CURVE_PROGRAM_ID
)

// Get token accounts
const ownerTokenAccount = getAssociatedTokenAddressSync(tokenMint, tokenOwnerPDA, true)

const userTokenAccount = getAssociatedTokenAddressSync(tokenMint, wallet.publicKey, false)

// Create instruction data buffer
const data = Buffer.alloc(18)
data.writeUint8(2, 0) // Instruction code for BuyToken
data.writeUint8(pdaTokenNonce, 1) // PDA nonce
data.writeBigUInt64LE(solToSpend, 2) // SOL amount
data.writeBigUInt64LE(minTokenToReceive, 10) // Minimum tokens
data.writeUint8(logAuthorityBump, 18) // Log authority bump

const buyTokenIx = new TransactionInstruction({
  programId: SC_BONDING_CURVE_PROGRAM_ID,
  keys: [
    { pubkey: wallet.publicKey, isSigner: true, isWritable: true },
    { pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
    { pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
    { pubkey: tokenOwnerPDA, isSigner: false, isWritable: true },
    { pubkey: tokenMint, isSigner: false, isWritable: false },
    { pubkey: ownerTokenAccount, isSigner: false, isWritable: true },
    { pubkey: userTokenAccount, isSigner: false, isWritable: true },
    { pubkey: FEE_ACCOUNT, isSigner: false, isWritable: true },
    { pubkey: propertiesAccount, isSigner: false, isWritable: false },
    { pubkey: SC_BONDING_CURVE_PROGRAM_ID, isSigner: false, isWritable: false },
    { pubkey: logAuthority, isSigner: false, isWritable: false },
  ],
  data: data,
})
```
