# TicketAutoCompoundVault

**Contract Address:** [`0x3E22Ea60A1206A4BfEefBCff04D5E9d0A2D9B3Fc`](https://basescan.org/address/0x3E22Ea60A1206A4BfEefBCff04D5E9d0A2D9B3Fc)

Enables users to automatically reinvest winning ticket prizes into new random tickets. Deposit winning tickets and compound winnings into new entries in a single transaction.

## Overview

The vault streamlines the reinvestment flow:

1. User approves the vault once via `setApprovalForAll`
2. User calls `depositAndCompound` with winning ticket IDs
3. Vault transfers tickets, claims winnings, and purchases new random tickets
4. Any remainder (less than one ticket's price) is stored for the next compound

This is ideal for third-party integrations that want to offer users a "reinvest winnings" feature without requiring multiple transactions.

{% hint style="info" %}
For a complete integration guide with pre-flight validation checks, see [Add Megapot to Your Site - Auto-Compound Winnings](/developers/buy-tickets.md#auto-compound-winnings).
{% endhint %}

## State Variables

| Variable            | Type                          | Description                                |
| ------------------- | ----------------------------- | ------------------------------------------ |
| `jackpot`           | `IJackpot`                    | Immutable reference to Jackpot contract    |
| `jackpotNFT`        | `IJackpotTicketNFT`           | Immutable reference to ticket NFT contract |
| `usdc`              | `IERC20`                      | Immutable USDC token reference             |
| `batchFacilitator`  | `IBatchPurchaseFacilitator`   | Batch facilitator for large orders         |
| `userPendingUSDC`   | `mapping(address => uint256)` | Accumulated USDC remainder per user        |
| `ticketPickerNonce` | `uint256`                     | Nonce for random ticket generation         |

## Events

### Compounded

```solidity
event Compounded(
    address indexed user,
    uint256 ticketsClaimed,
    uint256 usdcClaimed,
    uint256 ticketsBought,
    uint256 usdcSpent,
    uint256 usdcRemaining,
    bool useBatchPurchase,
    address[] referrers,
    uint256[] referralSplit
);
```

Emitted when a user successfully compounds winning tickets into new tickets.

| Field              | Description                                    |
| ------------------ | ---------------------------------------------- |
| `user`             | Address that performed the compound            |
| `ticketsClaimed`   | Number of winning tickets deposited            |
| `usdcClaimed`      | Total USDC claimed from winnings               |
| `ticketsBought`    | Number of new tickets purchased                |
| `usdcSpent`        | USDC spent on new tickets                      |
| `usdcRemaining`    | USDC stored for next compound                  |
| `useBatchPurchase` | Whether batch purchase was used (large orders) |
| `referrers`        | Referrer addresses for the purchase            |
| `referralSplit`    | Fee split weights for referrers                |

## Functions

### depositAndCompound

Compound winning tickets into new random tickets in a single transaction.

```solidity
function depositAndCompound(
    uint256[] calldata _ticketIds,
    address[] calldata _referrers,
    uint256[] calldata _referralSplit
) external
```

**Parameters:**

| Name             | Type        | Description                                                  |
| ---------------- | ----------- | ------------------------------------------------------------ |
| `_ticketIds`     | `uint256[]` | Array of winning ticket IDs to compound                      |
| `_referrers`     | `address[]` | Referrer addresses for fee sharing (can be empty)            |
| `_referralSplit` | `uint256[]` | PRECISE\_UNIT-scaled weights (must sum to 1e18 if non-empty) |

**Requirements:**

* User must have approved vault via `jackpotNFT.setApprovalForAll(vaultAddress, true)`
* All tickets must be owned by the caller
* At least one ticket must have winnings (from a completed drawing)
* Jackpot must not be locked (drawing in progress)
* User must not have an active batch order (if purchase would use batch path)

**Example:**

```typescript
// One-time setup: Approve vault to transfer your ticket NFTs
await jackpotNFT.setApprovalForAll(vaultAddress, true);

// Get winning ticket IDs (from a completed drawing)
const winningTicketIds = [123, 456, 789];

// Compound winnings into new tickets
const tx = await vault.depositAndCompound(
    winningTicketIds,
    [],  // no referrers
    []   // no splits
);

await tx.wait();
// New tickets minted to user (or batch order created for large amounts)
```

**With Referrers:**

```typescript
// Compound with referrer fees
const tx = await vault.depositAndCompound(
    winningTicketIds,
    [referrerAddress],           // your referrer address
    [BigInt(1e18)]               // 100% to single referrer
);
```

***

### getUserPendingUSDC

Get the pending USDC balance for a user (remainder from previous compounds).

```solidity
function getUserPendingUSDC(address _user) external view returns (uint256)
```

**Parameters:**

| Name    | Type      | Description      |
| ------- | --------- | ---------------- |
| `_user` | `address` | Address to query |

**Returns:**

| Type      | Description                       |
| --------- | --------------------------------- |
| `uint256` | Pending USDC balance (6 decimals) |

**Example:**

```typescript
const pendingUSDC = await vault.getUserPendingUSDC(userAddress);
const pendingAmount = Number(pendingUSDC) / 1e6;
console.log(`User has $${pendingAmount} pending for next compound`);
```

***

## Compound Flow

```mermaid
flowchart TB
    subgraph User["User Action"]
        A[Call depositAndCompound<br/>with winning ticket IDs]
    end

    subgraph Vault["TicketAutoCompoundVault"]
        B[Transfer tickets to vault]
        C[Claim winnings from Jackpot]
        D{Tickets to buy<br/>>= 10?}
        E[Direct purchase via<br/>Jackpot.buyTickets]
        F[Create batch order via<br/>BatchPurchaseFacilitator]
        G[Store remainder in<br/>userPendingUSDC]
    end

    subgraph Result["Result"]
        H[New ticket NFTs<br/>minted to user]
        I[Batch order queued<br/>for keeper execution]
    end

    A --> B --> C --> D
    D -->|No| E --> G --> H
    D -->|Yes| F --> G --> I
```

### Purchase Paths

The vault automatically selects the optimal purchase path:

| Scenario      | Path                            | Execution                            |
| ------------- | ------------------------------- | ------------------------------------ |
| < 10 tickets  | Direct via `Jackpot.buyTickets` | Immediate, tickets minted in same tx |
| >= 10 tickets | Via `BatchPurchaseFacilitator`  | Queued, keepers execute over time    |

### Pending USDC Accumulation

When winnings don't divide evenly by ticket price, the remainder accumulates:

```
Example:
- Drawing ticket price: $1.00
- User compounds ticket worth $2.50
- Previous pending: $0.30
- Total available: $2.80
- Tickets purchased: 2 ($2.00)
- New pending: $0.80 (carries to next compound)
```

## Integration Guide

### Setup (One-Time)

Before users can compound, they must approve the vault:

```typescript
// Check if already approved
const isApproved = await jackpotNFT.isApprovedForAll(userAddress, vaultAddress);

if (!isApproved) {
    const tx = await jackpotNFT.setApprovalForAll(vaultAddress, true);
    await tx.wait();
}
```

### Finding Winning Tickets

```typescript
async function getWinningTickets(userAddress: string, drawingId: number) {
    // Get user's tickets for the drawing
    const tickets = await ticketNFT.getUserTickets(userAddress, drawingId);
    const ticketIds = tickets.map(t => t.ticketId);

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

    // Filter to winning tickets (tier > 0)
    return ticketIds.filter((_, i) => Number(tierIds[i]) > 0);
}
```

### Complete Compound Flow

```typescript
async function compoundWinnings(
    userAddress: string,
    drawingId: number,
    referrerAddress?: string
) {
    // 1. Ensure approval
    const isApproved = await jackpotNFT.isApprovedForAll(userAddress, vaultAddress);
    if (!isApproved) {
        throw new Error('User must approve vault first');
    }

    // 2. Get winning tickets
    const winningTicketIds = await getWinningTickets(userAddress, drawingId);
    if (winningTicketIds.length === 0) {
        return { message: 'No winning tickets to compound' };
    }

    // 3. Check for active batch order (would block large compounds)
    const hasActiveBatch = await batchFacilitator.hasActiveBatchOrder(userAddress);
    if (hasActiveBatch) {
        throw new Error('User has active batch order - wait for completion');
    }

    // 4. Compound
    const referrers = referrerAddress ? [referrerAddress] : [];
    const splits = referrerAddress ? [BigInt(1e18)] : [];

    const tx = await vault.depositAndCompound(
        winningTicketIds,
        referrers,
        splits
    );

    const receipt = await tx.wait();

    // 5. Parse result from Compounded event
    const event = receipt.logs.find(
        log => log.topics[0] === vault.interface.getEvent('Compounded').topicHash
    );
    const decoded = vault.interface.parseLog(event);

    return {
        ticketsClaimed: Number(decoded.args.ticketsClaimed),
        usdcClaimed: Number(decoded.args.usdcClaimed) / 1e6,
        ticketsBought: Number(decoded.args.ticketsBought),
        usdcRemaining: Number(decoded.args.usdcRemaining) / 1e6,
        usedBatchPurchase: decoded.args.useBatchPurchase
    };
}
```

### Displaying Pending Balance

```typescript
async function displayCompoundStatus(userAddress: string) {
    const pendingUSDC = await vault.getUserPendingUSDC(userAddress);
    const ticketPrice = (await jackpot.getDrawingState(
        await jackpot.currentDrawingId()
    )).ticketPrice;

    const pendingAmount = Number(pendingUSDC) / 1e6;
    const priceAmount = Number(ticketPrice) / 1e6;
    const neededForTicket = priceAmount - pendingAmount;

    console.log(`Pending balance: $${pendingAmount.toFixed(2)}`);
    console.log(`Current ticket price: $${priceAmount.toFixed(2)}`);

    if (pendingAmount > 0 && neededForTicket > 0) {
        console.log(`Need $${neededForTicket.toFixed(2)} more winnings for next ticket`);
    }
}
```

## Error Handling

| Error                        | Cause                          | Solution                                           |
| ---------------------------- | ------------------------------ | -------------------------------------------------- |
| `EmptyTicketArray`           | Called with no ticket IDs      | Provide at least one ticket ID                     |
| `InvalidClaimedAmount`       | No tickets had winnings        | Ensure tickets are winners from completed drawings |
| `ActiveBatchOrderExists`     | User has pending batch order   | Wait for batch order to complete or cancel it      |
| `JackpotLocked`              | Drawing is in progress         | Wait for drawing to complete                       |
| `NotTicketOwner`             | Caller doesn't own the tickets | Only compound your own tickets                     |
| `TransferFromIncorrectOwner` | NFT transfer failed            | Ensure vault is approved via setApprovalForAll     |

## Best Practices

1. **Batch old drawings**: Compound tickets from multiple completed drawings at once to save gas
2. **Check active batch orders**: Before compounding large amounts, verify the user doesn't have an active batch order
3. **Display pending balance**: Show users their accumulated pending USDC so they understand the system
4. **Handle batch path**: If `useBatchPurchase` is true, inform users their tickets will be minted by keepers (not immediately)
5. **Referrer integration**: Pass your referrer address to earn fees on the new ticket purchases


---

# 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/contract-overview/ticket-auto-compound-vault.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.
