---
name: megapot-lp-withdraw
description: Two-step LP withdrawal from Megapot — initiateWithdraw locks shares for one drawing cycle, then finalizeWithdraw transfers USDC after settlement.
---

# LP Withdraw (Remove USDC Liquidity)

## What This Does

Removes USDC liquidity from the Megapot jackpot pool in two separate on-chain transactions:

1. **Step 1 — `initiateWithdraw(shares)`**: Locks a share amount for withdrawal. The locked shares are removed from active liquidity and held in a pending state until the current drawing settles.
2. **Step 2 — `finalizeWithdraw()`**: Claims the USDC after at least one drawing has settled since the initiation. The contract converts the locked shares back to USDC at the settled share price and transfers the funds to your wallet.

Both steps call through the **Jackpot** contract. There is no direct interaction with `JackpotLPManager` — it is called internally by Jackpot.

> **Cooldown**: Step 1 and Step 2 happen in separate transactions, typically hours to days apart. The drawing that is active when you call `initiateWithdraw` must fully settle before `finalizeWithdraw` will succeed. Drawing cycles are roughly 24 hours on mainnet.

## Prerequisites

- A wallet with a private key (EOA) connected to Base mainnet (chain ID 8453)
- Active LP shares (non-zero `consolidatedShares` in `getLpInfo`)
- No active pending withdrawal already in flight (`pendingWithdrawal.amountInShares` must be `0`)
- Sufficient ETH for gas
- `viem` installed (`npm install viem`)

## Addresses

Base mainnet (chain ID 8453):

```ts
const JACKPOT_ADDRESS    = '0x3bAe643002069dBCbcd62B1A4eb4C4A397d042a2' as const;
const LP_MANAGER_ADDRESS = '0xE63E54DF82d894396B885CE498F828f2454d9dCf' as const;
```

Base Sepolia testnet (chain ID 84532):

```ts
const JACKPOT_ADDRESS    = '0x465dA3c859f193A3807386387bEE941B2A4c3279' as const;
const LP_MANAGER_ADDRESS = '0x36408921aB820305F109150003C0F90aE1CB1766' as const;
```

> Base mainnet public RPC: `https://mainnet.base.org` (rate-limited; use [Alchemy](https://www.alchemy.com/) or [QuickNode](https://www.quicknode.com/) for production).

## ABI

Only the fragments needed for this task:

```ts
import { parseAbi } from 'viem';

const abi = parseAbi([
  // --- Jackpot writes ---
  "function initiateWithdraw(uint256 _amountToWithdrawInShares)",
  "function finalizeWithdraw()",

  // --- Jackpot reads ---
  "function currentDrawingId() view returns (uint256)",

  // --- Jackpot errors ---
  "error WithdrawAmountZero()",
  "error InsufficientShares()",
  "error NothingToWithdraw()",
  "error LPDepositsNotInitialized()",
  "error EmergencyEnabled()",
  "error JackpotLocked()",

  // --- JackpotLPManager reads ---
  "function getLpInfo(address _lpAddress) view returns ((uint256 consolidatedShares, (uint256 amount, uint256 drawingId) lastDeposit, (uint256 amountInShares, uint256 drawingId) pendingWithdrawal, uint256 claimableWithdrawals))",
  "function getLPValueBreakdown(address _lpAddress) view returns ((uint256 activeDeposits, uint256 pendingDeposits, uint256 pendingWithdrawals, uint256 claimableWithdrawals) breakdown)",
  "function getLPDrawingState(uint256 _drawingId) view returns ((uint256 lpPoolTotal, uint256 pendingDeposits, uint256 pendingWithdrawals))",
  "function getLPShares(address _lp) view returns (uint256)",

  // --- JackpotLPManager events ---
  "event LpWithdrawInitiated(address indexed lpAddress, uint256 indexed currentDrawingId, uint256 amount, uint256 totalPendingWithdrawals)",
  "event LpWithdrawFinalized(address indexed lpAddress, uint256 indexed currentDrawingId, uint256 amount)",
]);
```

## Recipe

### Setup

```ts
import {
  createPublicClient,
  createWalletClient,
  http,
  parseAbi,
} from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
import { base } from 'viem/chains';

// --- PLACEHOLDERS: replace with your values ---
const PRIVATE_KEY = '0xYOUR_PRIVATE_KEY' as `0x${string}`; // placeholder
const RPC_URL     = 'https://mainnet.base.org';             // or your own RPC URL

const JACKPOT_ADDRESS    = '0x3bAe643002069dBCbcd62B1A4eb4C4A397d042a2' as const;
const LP_MANAGER_ADDRESS = '0xE63E54DF82d894396B885CE498F828f2454d9dCf' as const;

// For Base Sepolia testnet: replace `base` with `baseSepolia` from 'viem/chains',
// swap addresses to the testnet values above, and use https://sepolia.base.org as RPC.

const abi = parseAbi([
  "function initiateWithdraw(uint256 _amountToWithdrawInShares)",
  "function finalizeWithdraw()",
  "function currentDrawingId() view returns (uint256)",
  "error WithdrawAmountZero()",
  "error InsufficientShares()",
  "error NothingToWithdraw()",
  "error LPDepositsNotInitialized()",
  "error EmergencyEnabled()",
  "error JackpotLocked()",
  "function getLpInfo(address _lpAddress) view returns ((uint256 consolidatedShares, (uint256 amount, uint256 drawingId) lastDeposit, (uint256 amountInShares, uint256 drawingId) pendingWithdrawal, uint256 claimableWithdrawals))",
  "function getLPValueBreakdown(address _lpAddress) view returns ((uint256 activeDeposits, uint256 pendingDeposits, uint256 pendingWithdrawals, uint256 claimableWithdrawals) breakdown)",
  "function getLPDrawingState(uint256 _drawingId) view returns ((uint256 lpPoolTotal, uint256 pendingDeposits, uint256 pendingWithdrawals))",
  "function getLPShares(address _lp) view returns (uint256)",
  "event LpWithdrawInitiated(address indexed lpAddress, uint256 indexed currentDrawingId, uint256 amount, uint256 totalPendingWithdrawals)",
  "event LpWithdrawFinalized(address indexed lpAddress, uint256 indexed currentDrawingId, uint256 amount)",
]);

const account = privateKeyToAccount(PRIVATE_KEY);

const publicClient = createPublicClient({
  chain: base,
  transport: http(RPC_URL),
});

const walletClient = createWalletClient({
  account,
  chain: base,
  transport: http(RPC_URL),
});
```

---

## Step 1 — Initiate Withdrawal (lock 50% of active shares)

This step locks shares for withdrawal. The locked shares stop earning yield immediately and will be redeemable for USDC after the current drawing settles.

### Step 1a — Read current share position

```ts
const lpInfo = await publicClient.readContract({
  address: LP_MANAGER_ADDRESS,
  abi,
  functionName: 'getLpInfo',
  args: [account.address],
});

console.log('Consolidated shares:      ', lpInfo.consolidatedShares);
console.log('Pending withdrawal shares:', lpInfo.pendingWithdrawal.amountInShares);
console.log('Pending withdrawal drawId:', lpInfo.pendingWithdrawal.drawingId);
console.log('Claimable withdrawals:    ', lpInfo.claimableWithdrawals);

if (lpInfo.consolidatedShares === 0n) {
  throw new Error('No active shares to withdraw.');
}

if (lpInfo.pendingWithdrawal.amountInShares > 0n) {
  throw new Error(
    `A withdrawal of ${lpInfo.pendingWithdrawal.amountInShares} shares is already pending ` +
    `(queued at drawing ${lpInfo.pendingWithdrawal.drawingId}). ` +
    `Call finalizeWithdraw() after that drawing settles.`
  );
}
```

### Step 1b — Estimate current USDC value of shares

Shares do not have a fixed 1:1 USDC value — they appreciate as the pool earns LP fees. Use `getLPValueBreakdown` to read the current breakdown in USDC terms:

```ts
const breakdown = await publicClient.readContract({
  address: LP_MANAGER_ADDRESS,
  abi,
  functionName: 'getLPValueBreakdown',
  args: [account.address],
});

// All values are in USDC raw units (6 decimals)
console.log('Active deposits (USDC): ', breakdown.activeDeposits);
console.log('Pending deposits (USDC):', breakdown.pendingDeposits);
console.log('Pending withdrawals:    ', breakdown.pendingWithdrawals);
console.log('Claimable withdrawals:  ', breakdown.claimableWithdrawals);

// The actual USDC received on finalize is determined at settlement time.
// breakdown.activeDeposits / consolidatedShares gives approximate price-per-share.
const pricePerShareUsdc =
  lpInfo.consolidatedShares > 0n
    ? (breakdown.activeDeposits * 10n ** 18n) / lpInfo.consolidatedShares
    : 0n;
console.log(
  'Approx price per share (18-dec scaled):',
  pricePerShareUsdc,
  '→',
  Number(pricePerShareUsdc) / 1e18,
  'USDC per share'
);
```

### Step 1c — Calculate 50% of active shares and initiate

```ts
// Withdraw 50% of consolidated shares
const sharesToWithdraw = lpInfo.consolidatedShares / 2n;

console.log(`Initiating withdrawal of ${sharesToWithdraw} shares (50% of ${lpInfo.consolidatedShares})`);

const initiateTx = await walletClient.writeContract({
  address: JACKPOT_ADDRESS,
  abi,
  functionName: 'initiateWithdraw',
  args: [sharesToWithdraw],
});

const initiateReceipt = await publicClient.waitForTransactionReceipt({ hash: initiateTx });
console.log('initiateWithdraw tx:', initiateTx);
console.log('Status:', initiateReceipt.status); // 'success'
```

### Step 1d — Confirm pending withdrawal

```ts
const drawingId = await publicClient.readContract({
  address: JACKPOT_ADDRESS,
  abi,
  functionName: 'currentDrawingId',
});

const lpInfoAfter = await publicClient.readContract({
  address: LP_MANAGER_ADDRESS,
  abi,
  functionName: 'getLpInfo',
  args: [account.address],
});

console.log('Pending withdrawal shares:', lpInfoAfter.pendingWithdrawal.amountInShares);
console.log('Locked at drawing:        ', lpInfoAfter.pendingWithdrawal.drawingId);
console.log('Current drawing:          ', drawingId);
console.log(
  'Drawing must settle before finalizing:',
  lpInfoAfter.pendingWithdrawal.drawingId
);
// Wait for drawingId >= pendingWithdrawal.drawingId + 1 before calling finalizeWithdraw
```

> **What to do now**: Wait. The drawing locked in `pendingWithdrawal.drawingId` must fully settle (entropy callback received, `JackpotSettled` event emitted) before you can call `finalizeWithdraw()`. On testnet this is typically minutes to hours; on mainnet it is roughly 24 hours per drawing cycle.

---

## Step 2 — Finalize Withdrawal (claim USDC after settlement)

Call this only after the drawing that was active during Step 1 has settled. The contract will convert your locked shares to USDC at the settled pool value and transfer them to your wallet.

### Step 2a — Check if withdrawal is claimable

```ts
const lpInfoCurrent = await publicClient.readContract({
  address: LP_MANAGER_ADDRESS,
  abi,
  functionName: 'getLpInfo',
  args: [account.address],
});

const currentDrawingId = await publicClient.readContract({
  address: JACKPOT_ADDRESS,
  abi,
  functionName: 'currentDrawingId',
});

console.log('Current drawing ID:       ', currentDrawingId);
console.log('Pending withdrawal drawId:', lpInfoCurrent.pendingWithdrawal.drawingId);
console.log('Claimable USDC:           ', lpInfoCurrent.claimableWithdrawals);

// The withdrawal is claimable once the drawing has advanced past the locked drawingId.
// claimableWithdrawals > 0 is the clearest signal.
const isClaimable =
  lpInfoCurrent.pendingWithdrawal.amountInShares > 0n &&
  currentDrawingId > lpInfoCurrent.pendingWithdrawal.drawingId;

if (!isClaimable) {
  throw new Error(
    `Withdrawal not yet claimable. ` +
    `Current drawing ${currentDrawingId}, locked at drawing ${lpInfoCurrent.pendingWithdrawal.drawingId}. ` +
    `Wait for the locked drawing to settle.`
  );
}

console.log('Withdrawal is claimable. Proceeding to finalize...');
```

### Step 2b — Finalize and receive USDC

```ts
const finalizeTx = await walletClient.writeContract({
  address: JACKPOT_ADDRESS,
  abi,
  functionName: 'finalizeWithdraw',
});

const finalizeReceipt = await publicClient.waitForTransactionReceipt({ hash: finalizeTx });
console.log('finalizeWithdraw tx:', finalizeTx);
console.log('Status:', finalizeReceipt.status); // 'success'
```

### Step 2c — Confirm USDC received

```ts
// Parse the LpWithdrawFinalized event from the receipt to see the exact USDC amount transferred
const finalizedEvent = finalizeReceipt.logs.find(log => {
  try {
    // LpWithdrawFinalized topic0
    return log.topics[0] ===
      '0x' + /* topic of LpWithdrawFinalized — use decodeEventLog with the abi in practice */ '';
  } catch {
    return false;
  }
});

// Simpler: re-read lpInfo — claimableWithdrawals should now be 0 and shares reduced
const lpInfoFinal = await publicClient.readContract({
  address: LP_MANAGER_ADDRESS,
  abi,
  functionName: 'getLpInfo',
  args: [account.address],
});

console.log('Remaining shares:         ', lpInfoFinal.consolidatedShares);
console.log('Pending withdrawal shares:', lpInfoFinal.pendingWithdrawal.amountInShares); // should be 0
console.log('Claimable withdrawals:    ', lpInfoFinal.claimableWithdrawals);             // should be 0
```

---

## Parameters

| Function | Parameter | Type | Description |
|---|---|---|---|
| `initiateWithdraw` | `_amountToWithdrawInShares` | `uint256` | Number of LP shares to lock for withdrawal. Must be > 0 and ≤ `consolidatedShares`. |
| `finalizeWithdraw` | _(none)_ | — | No parameters. Claims the entire pending withdrawal for `msg.sender`. |

## Key Concepts

- **Share price appreciation**: Shares accumulate value as the pool earns LP fees each drawing. The USDC you receive on finalize may exceed (or in rare loss scenarios be less than) your original deposit.
- **One pending withdrawal at a time**: `pendingWithdrawal.amountInShares` must be `0` before calling `initiateWithdraw` again. Finalize or wait for the current pending withdrawal first.
- **Cooldown enforced on-chain**: `finalizeWithdraw` reverts with `NothingToWithdraw()` if no settlement has occurred since `initiateWithdraw`. There is no way to bypass this — it is enforced by the drawing ID advancing.
- **No USDC approval needed**: Withdrawal sends USDC from the pool to the caller's address — no `approve` step is required.
- **Emergency mode**: If `emergencyMode` is active, use `Jackpot.emergencyWithdrawLP()` instead; the normal two-step flow is halted.

## Common Errors

| Error | Cause |
|---|---|
| `WithdrawAmountZero()` | `_amountToWithdrawInShares` is `0` |
| `InsufficientShares()` | Requested shares exceed `consolidatedShares` |
| `NothingToWithdraw()` | `finalizeWithdraw` called before the locked drawing has settled, or no pending withdrawal exists |
| `LPDepositsNotInitialized()` | LP deposit system has not been initialized by the contract owner |
| `EmergencyEnabled()` | Contract is in emergency mode; use `emergencyWithdrawLP()` instead |
| `JackpotLocked()` | Drawing is in the lock period immediately before a draw; retry after settlement |

## Timeline Summary

```
[NOW]   Step 1: initiateWithdraw(sharesToWithdraw)
         → shares locked, pendingWithdrawal.drawingId = currentDrawingId

[WAIT]  Drawing settles (entropy callback → JackpotSettled event)
         → currentDrawingId increments

[LATER] Step 2: finalizeWithdraw()
         → USDC transferred to your wallet
         → pendingWithdrawal cleared
```

## Related

- `megapot-buy-tickets` — buy 1–10 tickets with custom numbers
- `megapot-buy-random` — buy up to 10 random tickets (no number selection needed)
- `megapot-buy-bulk` — buy 11+ tickets with custom numbers (batch)
- `megapot-lp-deposit` — deposit USDC to receive LP shares (the complementary skill)
- `megapot-contracts-reference` — full address table and complete ABI for all contracts
