# Add Megapot to Your Site

This guide walks you through integrating Megapot into your dApp. Follow these steps to build a complete lottery experience:

1. [Display Prize Information](#display-prize-information) - Show users the current jackpot and prize tiers
2. [Buy Tickets](#buy-tickets) - Let users purchase tickets
3. [Display User Tickets](#display-user-tickets) - Show users their ticket holdings
4. [Claim Winnings](#claim-winnings) - Allow users to claim their prizes
5. [Auto-Compound Winnings](#auto-compound-winnings) - Let users reinvest winnings into new tickets

For contract addresses and ABIs, see the [Contract Overview](/developers/contract-overview.md).

***

## Display Prize Information

Show users the current jackpot and prize tier payouts.

**Contracts:**

* Jackpot: `0x3bAe643002069dBCbcd62B1A4eb4C4A397d042a2`
* PayoutCalculator: `0x97a22361b6208aC8cd9afaea09D20feC47046CBD`

### Get Current Drawing Info

```typescript
const drawingId = await jackpot.currentDrawingId();
const state = await jackpot.getDrawingState(drawingId);

const currentJackpot = Number(state.prizePool) / 1e6;  // USDC amount
const ticketPrice = Number(state.ticketPrice) / 1e6;   // USDC amount
const bonusballMax = Number(state.bonusballMax);
```

### Fetch All Prize Tier Payouts

```typescript
const payouts = await payoutCalculator.getExpectedDrawingTierPayouts(
    drawingId,
    state.prizePool,
    state.ballMax,
    state.bonusballMax
);

// Format for display (payouts are in USDC wei, 6 decimals)
const prizes = {
    jackpot: Number(payouts[11]) / 1e6,        // 5 matches + bonusball
    fiveMatch: Number(payouts[10]) / 1e6,      // 5 matches
    fourPlusBonus: Number(payouts[9]) / 1e6,   // 4 matches + bonusball
    fourMatch: Number(payouts[8]) / 1e6,       // 4 matches
    threePlusBonus: Number(payouts[7]) / 1e6,  // 3 matches + bonusball
    threeMatch: Number(payouts[6]) / 1e6,      // 3 matches
    twoPlusBonus: Number(payouts[5]) / 1e6,    // 2 matches + bonusball
    twoMatch: Number(payouts[4]) / 1e6,        // 2 matches
    onePlusBonus: Number(payouts[3]) / 1e6,    // 1 match + bonusball
    bonusOnly: Number(payouts[1]) / 1e6,       // bonusball only
};
```

### Prize Tier Mapping

The 12 tiers are calculated as `(normalMatches * 2) + bonusballMatch`:

| Tier | Matches | Bonusball | Description             |
| ---- | ------- | --------- | ----------------------- |
| 1    | 0       | Yes       | Bonusball only          |
| 3    | 1       | Yes       | 1 match + bonusball     |
| 4    | 2       | No        | 2 matches               |
| 5    | 2       | Yes       | 2 matches + bonusball   |
| 6    | 3       | No        | 3 matches               |
| 7    | 3       | Yes       | 3 matches + bonusball   |
| 8    | 4       | No        | 4 matches               |
| 9    | 4       | Yes       | 4 matches + bonusball   |
| 10   | 5       | No        | 5 matches               |
| 11   | 5       | Yes       | Jackpot (5 + bonusball) |

Tiers 0 and 2 (0 matches and 1 match without bonusball) have no payout.

For complete details on the payout calculation system, see [PayoutCalculator](/developers/contract-overview/payout-calculator.md).

***

## Buy Tickets

Choose the right purchase method based on your use case.

### Quick Reference

| Scenario                     | Contract                                                                 | Execution       | Ticket Types     |
| ---------------------------- | ------------------------------------------------------------------------ | --------------- | ---------------- |
| <=10 tickets, single drawing | [`Jackpot`](#direct-purchase-jackpotbuytickets)                          | Immediate       | Static only      |
| <=10 tickets, random numbers | [`JackpotRandomTicketBuyer`](#random-tickets-jackpotrandomticketbuyer)   | Immediate       | Dynamic only     |
| >10 tickets, single drawing  | [`BatchPurchaseFacilitator`](#batch-purchase-batchpurchasefacilitator)   | Keeper-executed | Static + Dynamic |
| Multiple drawings            | [`JackpotAutoSubscription`](#recurring-purchase-jackpotautosubscription) | Keeper-executed | Static + Dynamic |

### Key Concepts

#### Static vs Dynamic Tickets

**Static tickets** are user-defined number combinations. Your dApp specifies exactly which 5 normal numbers and bonusball to play.

**Dynamic tickets** are randomly generated on-chain. The contract generates valid random numbers at execution time.

#### Ticket Structure

All methods use the same ticket format:

```typescript
interface Ticket {
    normals: number[];  // 5 unique numbers in [1, 30], sorted ascending
    bonusball: number;  // 1 number in [1, bonusballMax]
}
```

The `bonusballMax` varies by drawing - read it from `getDrawingState()`.

#### Recommended Limits

When using `BatchPurchaseFacilitator` or `JackpotAutoSubscription`:

* **Limit static tickets to 100 per order**
* If purchasing more than 100 tickets, make the rest dynamic

#### USDC Approval

Before purchasing, approve USDC spending on the correct contract:

```typescript
const ticketCount = 5;
const totalCost = ticketPrice * BigInt(ticketCount);

// Approve the contract that will receive the payment
await usdc.approve(contractAddress, totalCost);
```

| Method                                       | Approve USDC to          |
| -------------------------------------------- | ------------------------ |
| `Jackpot.buyTickets`                         | Jackpot                  |
| `JackpotRandomTicketBuyer.buyTickets`        | JackpotRandomTicketBuyer |
| `BatchPurchaseFacilitator.createBatchOrder`  | BatchPurchaseFacilitator |
| `JackpotAutoSubscription.createSubscription` | JackpotAutoSubscription  |

***

### Direct Purchase: `Jackpot.buyTickets`

**When to use:** 1-10 tickets where you want control over number selection.

**Contract:** `0x3bAe643002069dBCbcd62B1A4eb4C4A397d042a2`

```typescript
// 1. Get ticket price
const drawingId = await jackpot.currentDrawingId();
const state = await jackpot.getDrawingState(drawingId);
const ticketPrice = state.ticketPrice;

// 2. Define tickets
const tickets = [
    { normals: [7, 14, 21, 28, 30], bonusball: 12 },
    { normals: [3, 11, 19, 27, 29], bonusball: 8 }
];

// 3. Approve USDC
const totalCost = ticketPrice * BigInt(tickets.length);
await usdc.approve(jackpotAddress, totalCost);

// 4. Buy tickets
const tx = await jackpot.buyTickets(
    tickets,
    userAddress,      // recipient
    [],               // referrers (empty if none)
    [],               // referralSplit (empty if none)
    "0x0000000000000000000000000000000000000000000000000000000000000000"  // source
);

const receipt = await tx.wait();
// Tickets are minted immediately
```

#### Parameters

| Parameter        | Type        | Description                                                                                  |
| ---------------- | ----------- | -------------------------------------------------------------------------------------------- |
| `_tickets`       | `Ticket[]`  | Array of tickets (max 10). Each has `normals` (5 unique sorted numbers 1-30) and `bonusball` |
| `_recipient`     | `address`   | Address that will own the ticket NFTs                                                        |
| `_referrers`     | `address[]` | Referrer addresses for fee sharing. Pass `[]` if none                                        |
| `_referralSplit` | `uint256[]` | Weight for each referrer, scaled to 1e18. Pass `[]` if none                                  |
| `_source`        | `bytes32`   | Identifier for your application                                                              |

{% hint style="info" %}
To earn referrer fees, see [How to Earn by Referring](/developers/refer-to-earn.md) for details on the `_referrers` and `_referralSplit` parameters.
{% endhint %}

***

### Random Tickets: `JackpotRandomTicketBuyer`

**When to use:** Quick purchases where users don't care about specific numbers.

**Contract:** `0xb9560b43b91dE2c1DaF5dfbb76b2CFcDaFc13aBd`

```typescript
// 1. Get ticket price
const drawingId = await jackpot.currentDrawingId();
const state = await jackpot.getDrawingState(drawingId);
const ticketPrice = state.ticketPrice;

// 2. Approve USDC
const ticketCount = 5;
const totalCost = ticketPrice * BigInt(ticketCount);
await usdc.approve(randomTicketBuyerAddress, totalCost);

// 3. Buy random tickets
const tx = await randomTicketBuyer.buyTickets(
    ticketCount,
    userAddress,      // recipient
    [],               // referrers
    [],               // referralSplit
    "0x0000000000000000000000000000000000000000000000000000000000000000"  // source
);

const receipt = await tx.wait();
// Tickets with random numbers are minted immediately
```

***

### Batch Purchase: `BatchPurchaseFacilitator`

**When to use:** More than 10 tickets in a single drawing.

**Contract:** `0x01774B531591b286b9f02C6Bc02ab3fD9526Aa76`

Batch orders are prepaid and executed by protocol keepers. Supports a mix of static and dynamic tickets.

```typescript
// 1. Get ticket price
const drawingId = await jackpot.currentDrawingId();
const state = await jackpot.getDrawingState(drawingId);
const ticketPrice = state.ticketPrice;

// 2. Define order
const dynamicTicketCount = 40;  // Random tickets
const staticTickets = [
    { normals: [1, 2, 3, 4, 5], bonusball: 1 },
    { normals: [6, 7, 8, 9, 10], bonusball: 2 }
];

// 3. Approve USDC
const totalTickets = dynamicTicketCount + staticTickets.length;
const totalCost = ticketPrice * BigInt(totalTickets);
await usdc.approve(batchFacilitatorAddress, totalCost);

// 4. Create batch order
const tx = await batchFacilitator.createBatchOrder(
    userAddress,        // recipient
    dynamicTicketCount,
    staticTickets,
    [],                 // referrers
    []                  // referralSplit
);

await tx.wait();
// Order is queued - keepers will execute and mint tickets
```

#### Cancellation

```typescript
// Cancel and get refund for unexecuted tickets
await batchFacilitator.cancelBatchOrder();
```

Orders are also auto-cancelled if the drawing locks before execution completes.

***

### Recurring Purchase: `JackpotAutoSubscription`

**When to use:** Tickets across multiple drawings.

**Contract:** `0x02A58B725116BA687D9356Eafe0fA771d58a37ac`

```typescript
// 1. Get ticket price (locked for subscription duration)
const drawingId = await jackpot.currentDrawingId();
const state = await jackpot.getDrawingState(drawingId);
const ticketPrice = state.ticketPrice;

// 2. Define subscription
const totalDays = 30;
const dynamicTicketsPerDay = 5;
const staticTickets = [];  // Same numbers play every day

// 3. Approve USDC
const ticketsPerDay = dynamicTicketsPerDay + staticTickets.length;
const totalCost = ticketPrice * BigInt(totalDays) * BigInt(ticketsPerDay);
await usdc.approve(autoSubscriptionAddress, totalCost);

// 4. Create subscription
const tx = await autoSubscription.createSubscription(
    userAddress,           // recipient
    totalDays,
    dynamicTicketsPerDay,
    staticTickets,
    [],                    // referrers
    []                     // referralSplit
);

await tx.wait();
// Subscription active - tickets purchased each drawing
```

#### Cancellation

```typescript
// Cancel and get refund for remaining days
await autoSubscription.cancelSubscription();
```

***

## Display User Tickets

Show users their ticket holdings.

{% hint style="info" %}
**Wallet-wide history?** For tickets across many drawings without writing per-drawing pagination, use the [Data API → wallet tickets](/developers/data-api.md) endpoint (`GET /v1/wallets/{address}/tickets`). The contract reads below remain the right path for current-drawing display and live updates.
{% endhint %}

**Contract:** JackpotTicketNFT (`0x48FfE35AbB9f4780a4f1775C2Ce1c46185b366e4`)

### Query Tickets for a Drawing

```typescript
const drawingId = await jackpot.currentDrawingId();
const tickets = await ticketNFT.getUserTickets(userAddress, drawingId);

const formattedTickets = tickets.map(t => ({
    id: t.ticketId.toString(),
    numbers: t.normals.map(n => Number(n)),
    bonusball: Number(t.bonusball),
    drawingId: Number(t.ticket.drawingId)
}));

console.log(formattedTickets);
// [{ id: "123", numbers: [7, 14, 21, 28, 30], bonusball: 12, drawingId: 42 }, ...]
```

### Query Tickets Across Multiple Drawings

```typescript
async function getAllUserTickets(userAddress: string, fromDrawingId: number, toDrawingId: number) {
    const allTickets = [];

    for (let drawingId = fromDrawingId; drawingId <= toDrawingId; drawingId++) {
        const tickets = await ticketNFT.getUserTickets(userAddress, drawingId);
        allTickets.push(...tickets.map(t => ({
            id: t.ticketId.toString(),
            numbers: t.normals.map(n => Number(n)),
            bonusball: Number(t.bonusball),
            drawingId
        })));
    }

    return allTickets;
}
```

### Get Single Ticket Details

```typescript
const ticket = await ticketNFT.getExtendedTicketInfo(ticketId);

console.log({
    id: ticket.ticketId.toString(),
    drawingId: Number(ticket.ticket.drawingId),
    numbers: ticket.normals.map(n => Number(n)),
    bonusball: Number(ticket.bonusball)
});
```

For complete ticket NFT documentation, see [JackpotTicketNFT](/developers/contract-overview/jackpot-ticket-nft.md).

***

## Claim Winnings

Allow users to claim prizes from winning tickets.

**Contract:** Jackpot (`0x3bAe643002069dBCbcd62B1A4eb4C4A397d042a2`)

### Complete Claim Flow

```typescript
async function claimWinnings(userAddress: string, drawingId: number) {
    // 1. Check drawing is complete
    const state = await jackpot.getDrawingState(drawingId);
    if (state.phase !== 4) { // 4 = COMPLETE
        throw new Error('Drawing not yet complete');
    }

    // 2. Get user's tickets for this drawing
    const tickets = await ticketNFT.getUserTickets(userAddress, drawingId);
    if (tickets.length === 0) {
        return { message: 'No tickets for this drawing' };
    }

    const ticketIds = tickets.map(t => t.ticketId);

    // 3. Check which tickets won
    const tierIds = await jackpot.getTicketTierIds(ticketIds);
    const tierPayouts = await jackpot.getDrawingTierPayouts(drawingId);

    // 4. Filter to winning tickets and calculate payout
    const winningTickets = [];
    let totalPayout = 0n;

    for (let i = 0; i < ticketIds.length; i++) {
        const tierId = Number(tierIds[i]);
        if (tierId > 0) {
            winningTickets.push(ticketIds[i]);
            totalPayout += tierPayouts[tierId];
        }
    }

    if (winningTickets.length === 0) {
        return { message: 'No winning tickets' };
    }

    // 5. Claim winnings (tickets are burned, USDC transferred)
    const tx = await jackpot.claimWinnings(winningTickets);
    await tx.wait();

    return {
        ticketsClaimed: winningTickets.length,
        totalPayout: Number(totalPayout) / 1e6  // USDC amount
    };
}
```

### Check Win Status Without Claiming

{% hint style="info" %}
**Cross-drawing unclaimed-wins detection?** For "show me all my unclaimed wins" UIs spanning many drawings, the [Data API → wallet wins](/developers/data-api.md) endpoint (`GET /v1/wallets/{address}/wins`) is simpler than the per-drawing scan below. Use the on-chain check below when you need to drive a write transaction (the actual claim).
{% endhint %}

```typescript
async function checkWinnings(userAddress: string, drawingId: number) {
    const tickets = await ticketNFT.getUserTickets(userAddress, drawingId);
    const ticketIds = tickets.map(t => t.ticketId);

    const tierIds = await jackpot.getTicketTierIds(ticketIds);
    const tierPayouts = await jackpot.getDrawingTierPayouts(drawingId);

    return tickets.map((ticket, i) => {
        const tierId = Number(tierIds[i]);
        return {
            ticketId: ticket.ticketId.toString(),
            numbers: ticket.normals.map(n => Number(n)),
            bonusball: Number(ticket.bonusball),
            won: tierId > 0,
            tier: tierId,
            payout: tierId > 0 ? Number(tierPayouts[tierId]) / 1e6 : 0
        };
    });
}
```

### Important Notes

* Tickets are burned when claimed - they cannot be claimed twice
* Only the ticket owner (or approved address) can claim
* For large numbers of tickets (>75), split claims across multiple transactions to avoid gas limits
* Winnings are paid in USDC to the address that calls `claimWinnings`

For complete Jackpot contract documentation, see [Jackpot](/developers/contract-overview/jackpot.md).

***

## Auto-Compound Winnings

Let users automatically reinvest their winnings into new tickets with a single transaction.

**Contract:** TicketAutoCompoundVault (`0x3E22Ea60A1206A4BfEefBCff04D5E9d0A2D9B3Fc`)

The auto-compound vault enables a "reinvest winnings" feature where users deposit winning tickets and receive new random tickets purchased with their prizes.

### Pre-Flight Checklist

Before calling `depositAndCompound`, verify these conditions to ensure successful execution:

```typescript
async function canCompound(userAddress: string, ticketIds: bigint[]) {
    const errors: string[] = [];

    // 1. User has approved vault for NFT transfers
    const isApproved = await jackpotNFT.isApprovedForAll(userAddress, vaultAddress);
    if (!isApproved) {
        errors.push('User must approve vault: jackpotNFT.setApprovalForAll(vaultAddress, true)');
    }

    // 2. Jackpot is not locked (no drawing in progress)
    const drawingId = await jackpot.currentDrawingId();
    const state = await jackpot.getDrawingState(drawingId);
    if (state.jackpotLock) {
        errors.push('Jackpot is locked - wait for drawing to complete');
    }

    // 3. Tickets have winnings (from completed drawings)
    const tierIds = await jackpot.getTicketTierIds(ticketIds);
    const hasWinners = tierIds.some(t => Number(t) > 0);
    if (!hasWinners) {
        errors.push('No winning tickets provided');
    }

    // 4. Estimate if batch path will be used, check for active batch order
    const tierPayouts = await jackpot.getDrawingTierPayouts(
        (await ticketNFT.getTicketInfo(ticketIds[0])).drawingId
    );
    let estimatedWinnings = 0n;
    for (let i = 0; i < ticketIds.length; i++) {
        const tierId = Number(tierIds[i]);
        if (tierId > 0) estimatedWinnings += tierPayouts[tierId];
    }

    const pendingUSDC = await vault.getUserPendingUSDC(userAddress);
    const totalUSDC = estimatedWinnings + pendingUSDC;
    const estimatedTickets = totalUSDC / state.ticketPrice;
    const minimumBatchCount = await batchFacilitator.minimumTicketCount(); // Currently 10

    if (estimatedTickets >= minimumBatchCount) {
        const hasActiveBatch = await batchFacilitator.hasActiveBatchOrder(userAddress);
        if (hasActiveBatch) {
            errors.push('User has active batch order - wait for completion or cancel it first');
        }
    }

    return {
        canProceed: errors.length === 0,
        errors,
        estimatedTickets: Number(estimatedTickets),
        willUseBatchPath: estimatedTickets >= minimumBatchCount
    };
}
```

{% hint style="warning" %}
**Critical**: If the compound will purchase >= 10 tickets, it routes through `BatchPurchaseFacilitator`. Users with an active batch order will get `ActiveBatchOrderExists` error. Always check `batchFacilitator.hasActiveBatchOrder()` first.
{% endhint %}

### When to Use

| Scenario                            | Recommended Approach                             |
| ----------------------------------- | ------------------------------------------------ |
| User wants to claim as USDC         | Use `Jackpot.claimWinnings`                      |
| User wants to reinvest all winnings | Use `TicketAutoCompoundVault.depositAndCompound` |
| User wants partial reinvestment     | Claim first, then buy tickets manually           |

### One-Time Setup

Users must approve the vault to transfer their ticket NFTs:

```typescript
const tx = await jackpotNFT.setApprovalForAll(vaultAddress, true);
await tx.wait();
```

### Compound Winning Tickets

```typescript
// 1. Run pre-flight checks
const check = await canCompound(userAddress, winningTicketIds);
if (!check.canProceed) {
    console.error('Cannot compound:', check.errors);
    return;
}

// 2. Compound winnings into new tickets
const tx = await vault.depositAndCompound(
    winningTicketIds,
    [],  // referrers (or pass your address to earn fees)
    []   // referralSplit
);

await tx.wait();

// 3. Handle result based on path
if (check.willUseBatchPath) {
    console.log('Batch order created - tickets will be minted by keepers');
} else {
    console.log(`${check.estimatedTickets} tickets minted immediately`);
}
```

### With Referrer Fees

Earn referral fees on the new tickets purchased:

```typescript
const tx = await vault.depositAndCompound(
    winningTicketIds,
    [yourReferrerAddress],  // your address
    [BigInt(1e18)]          // 100% to you
);
```

### Check Pending Balance

The vault stores any remainder (less than one ticket's worth) for the next compound:

```typescript
const pendingUSDC = await vault.getUserPendingUSDC(userAddress);
const pendingAmount = Number(pendingUSDC) / 1e6;

// Display to user
console.log(`$${pendingAmount.toFixed(2)} pending for next compound`);
```

### Error Reference

| Error                        | Cause                        | Solution                                                                 |
| ---------------------------- | ---------------------------- | ------------------------------------------------------------------------ |
| `EmptyTicketArray`           | No ticket IDs provided       | Pass at least one ticket ID                                              |
| `InvalidClaimedAmount`       | No tickets had winnings      | Verify tickets are winners from completed drawings                       |
| `ActiveBatchOrderExists`     | User has pending batch order | Wait for batch to complete or call `batchFacilitator.cancelBatchOrder()` |
| `JackpotLocked`              | Drawing in progress          | Wait for drawing to complete                                             |
| `TransferFromIncorrectOwner` | Vault not approved           | Call `jackpotNFT.setApprovalForAll(vaultAddress, true)`                  |

For complete contract documentation, see [TicketAutoCompoundVault](/developers/contract-overview/ticket-auto-compound-vault.md).


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.megapot.io/developers/buy-tickets.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
